RN 全局状态与单例工具完全指南

在 React Native 开发中,跨组件共享数据、监听全局事件或持久化配置是常见需求。本文系统梳理了从原生 API 到第三方库的常用全局解决方案,每个工具均附带完整的调用示例,可直接复制运行。

1. 单例模式(Singleton)

利用 JS 模块缓存,导出一个实例。适合存储不驱动 UI 的全局配置。

// config.js
class AppConfig {
  apiUrl = 'https://api.example.com';
  setApi(url) { this.apiUrl = url; }
}
export default new AppConfig();

调用示例:在组件中修改并读取配置

import React from 'react';
import { View, Text, Button } from 'react-native';
import appConfig from './config';

export default function ConfigDemo() {
  const [currentUrl, setCurrentUrl] = React.useState(appConfig.apiUrl);

  const changeUrl = () => {
    appConfig.setApi('https://new-api.example.com');
    setCurrentUrl(appConfig.apiUrl); // 手动刷新 UI(单例不会自动触发渲染)
  };

  return (
    <View>
      <Text>当前 API 地址:{currentUrl}</Text>
      <Button title="切换 API 地址" onPress={changeUrl} />
    </View>
  );
}

⚠️ 注意:单例变化不会触发 React 重渲染,需要手动刷新 UI,因此不适合管理驱动界面的状态。

2. React Native 内置 API

2.1 Dimensions / useWindowDimensions

获取屏幕尺寸,用于响应式布局。

调用示例:实时显示窗口宽高,旋转屏幕时自动更新

import React from 'react';
import { View, Text, useWindowDimensions, StyleSheet } from 'react-native';

export default function DimensionDemo() {
  const { width, height } = useWindowDimensions();

  return (
    <View style={[styles.container, { backgroundColor: width > height ? '#e0f7fa' : '#ffe0b5' }]}>
      <Text>窗口宽度:{Math.round(width)}px</Text>
      <Text>窗口高度:{Math.round(height)}px</Text>
      <Text>当前模式:{width > height ? '横屏' : '竖屏'}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
});

Dimensions.get('window') 获取静态值,不会自动更新;useWindowDimensions 会在屏幕旋转或折叠屏变化时自动触发重渲染。

2.2 AsyncStorage

持久化存储键值对数据(如用户 token、主题偏好)。

调用示例:保存用户昵称,重启 App 后仍然保留

import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, Button, Alert } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';

const STORAGE_KEY = '@user_nickname';

export default function AsyncStorageDemo() {
  const [nickname, setNickname] = useState('');
  const [savedName, setSavedName] = useState('');

  // 加载已保存的昵称
  useEffect(() => {
    const loadNickname = async () => {
      try {
        const stored = await AsyncStorage.getItem(STORAGE_KEY);
        if (stored !== null) setSavedName(stored);
      } catch (e) {
        Alert.alert('读取失败', e.message);
      }
    };
    loadNickname();
  }, []);

  const saveNickname = async () => {
    if (!nickname.trim()) return;
    try {
      await AsyncStorage.setItem(STORAGE_KEY, nickname);
      setSavedName(nickname);
      setNickname('');
      Alert.alert('保存成功', `昵称已更新为 ${nickname}`);
    } catch (e) {
      Alert.alert('保存失败', e.message);
    }
  };

  return (
    <View style={{ padding: 20 }}>
      <Text>已保存的昵称:{savedName || '未设置'}</Text>
      <TextInput
        style={{ borderWidth: 1, marginVertical: 10, padding: 8 }}
        placeholder="输入新昵称"
        value={nickname}
        onChangeText={setNickname}
      />
      <Button title="保存昵称" onPress={saveNickname} />
    </View>
  );
}

敏感信息请使用 react-native-encrypted-storage 替代。

2.3 DeviceEventEmitter

全局事件总线,适合无直接关系的组件通信,或接收原生模块事件。

调用示例:两个独立组件通过事件传递数据

import React, { useEffect, useState } from 'react';
import { View, Text, Button, DeviceEventEmitter } from 'react-native';

// 发送事件组件
function Sender() {
  const sendEvent = () => {
    DeviceEventEmitter.emit('greeting', { message: 'Hello from Sender!' });
  };
  return <Button title="发送全局事件" onPress={sendEvent} />;
}

// 接收事件组件
function Receiver() {
  const [lastMsg, setLastMsg] = useState('无');

  useEffect(() => {
    const subscription = DeviceEventEmitter.addListener('greeting', (data) => {
      setLastMsg(data.message);
    });
    return () => subscription.remove(); // 清理监听器
  }, []);

  return (
    <View>
      <Text>最后收到的消息:{lastMsg}</Text>
    </View>
  );
}

export default function EventDemo() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', gap: 20, padding: 20 }}>
      <Sender />
      <Receiver />
    </View>
  );
}

3. React 上下文与状态管理

3.1 Props 传递

React 基础通信方式,适合父子直传。

调用示例:父组件传递数据给子组件

import React from 'react';
import { View, Text } from 'react-native';

function Child({ userName }) {
  return <Text>子组件收到:{userName}</Text>;
}

export default function Parent() {
  const name = 'Alice';
  return (
    <View>
      <Text>父组件传递数据</Text>
      <Child userName={name} />
    </View>
  );
}

多层传递会导致 “props drilling”,维护困难,此时应使用 Context 或状态库。

3.2 Context API

解决 props 钻取,适合主题、语言、用户信息等低频全局数据。

调用示例:全局主题切换,任意子组件消费

import React, { useContext, useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

// 1. 创建 Context
const ThemeContext = React.createContext('light');

// 2. 提供 Context 的组件
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  const toggleTheme = () => setTheme(t => t === 'light' ? 'dark' : 'light');
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 3. 消费 Context 的深层组件
function ThemedText() {
  const { theme } = useContext(ThemeContext);
  const textColor = theme === 'light' ? '#000' : '#fff';
  const bgColor = theme === 'light' ? '#fff' : '#333';
  return (
    <View style={{ backgroundColor: bgColor, padding: 20 }}>
      <Text style={{ color: textColor }}>当前主题:{theme}</Text>
    </View>
  );
}

function ThemeToggleButton() {
  const { toggleTheme } = useContext(ThemeContext);
  return <Button title="切换主题" onPress={toggleTheme} />;
}

export default function ContextDemo() {
  return (
    <ThemeProvider>
      <View style={styles.container}>
        <ThemedText />
        <ThemeToggleButton />
      </View>
    </ThemeProvider>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
});

Context 值变化会导致所有消费者重渲染,高频变化时需注意性能。

3.3 第三方状态库(Zustand)

Zustand 提供极简 API,性能优秀,是 RN 社区热门选择。

调用示例:全局计数器,任意组件共享状态

import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { create } from 'zustand';

// 创建 store
const useCounterStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}));

// 组件 A - 显示和增加
function CounterDisplay() {
  const count = useCounterStore((state) => state.count);
  const increment = useCounterStore((state) => state.increment);
  return (
    <View style={styles.card}>
      <Text style={styles.count}>{count}</Text>
      <Button title="增加" onPress={increment} />
    </View>
  );
}

// 组件 B - 减少和重置
function CounterControls() {
  const decrement = useCounterStore((state) => state.decrement);
  const reset = useCounterStore((state) => state.reset);
  return (
    <View style={styles.card}>
      <Button title="减少" onPress={decrement} />
      <Button title="重置" onPress={reset} />
    </View>
  );
}

export default function ZustandDemo() {
  return (
    <View style={styles.container}>
      <CounterDisplay />
      <CounterControls />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center', gap: 20 },
  card: { alignItems: 'center', gap: 10 },
  count: { fontSize: 48, fontWeight: 'bold' },
});

其他备选:Redux Toolkit(适合超大型项目)、Jotai(原子化状态)。

4. 其他实用工具

4.1 global 对象

Node.js 风格的全局对象,极度不推荐用于业务状态。仅示例其调用方式:

// 设置(污染全局)
global.userId = '123';

// 读取
console.log(global.userId);

避免使用,除非调试或与遗留代码桥接。

4.2 PixelRatio

获取设备像素密度,用于精细适配。

调用示例:根据像素密度调整图片尺寸或边框宽度

import React from 'react';
import { View, Text, PixelRatio, StyleSheet } from 'react-native';

export default function PixelRatioDemo() {
  const scale = PixelRatio.get();
  const fontScale = PixelRatio.getFontScale();
  const onePixel = 1 / scale; // 最细线条宽度

  return (
    <View style={styles.container}>
      <Text>设备像素密度:{scale}</Text>
      <Text>字体缩放比例:{fontScale}</Text>
      <View style={{ borderWidth: onePixel, borderColor: 'red', padding: 10 }}>
        <Text>边框宽度 = 1 物理像素</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center', gap: 10 },
});

5. 快速选择指南

需求场景 推荐方案 示例章节
持久化存储(配置、token) AsyncStorage 2.2
屏幕尺寸响应 useWindowDimensions 2.1
跨组件事件通知 DeviceEventEmitter 2.3
低频全局数据(主题、语言) Context API 3.2
中大型应用 UI 状态 Zustand / Redux Toolkit 3.3
父传子简单数据 Props 3.1
绝对不要做 UI 状态管理 单例模式、global 对象 1, 4.1
精细适配(像素线宽) PixelRatio 4.2

总结

  • 驱动 UI 的状态 → Zustand(推荐)或 Context(低频数据)。
  • 持久化数据 → AsyncStorage。
  • 跨组件事件 → DeviceEventEmitter。
  • 屏幕适配 → useWindowDimensions。
  • 像素级精细适配 → PixelRatio。
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容