RN 全局狀態(tài)與單例工具完全指南

在 React Native 開發(fā)中,跨組件共享數(shù)據(jù)、監(jiān)聽全局事件或持久化配置是常見需求。本文系統(tǒng)梳理了從原生 API 到第三方庫的常用全局解決方案,每個(gè)工具均附帶完整的調(diào)用示例,可直接復(fù)制運(yùn)行。

1. 單例模式(Singleton)

利用 JS 模塊緩存,導(dǎo)出一個(gè)實(shí)例。適合存儲(chǔ)不驅(qū)動(dòng) UI 的全局配置。

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

調(diào)用示例:在組件中修改并讀取配置

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); // 手動(dòng)刷新 UI(單例不會(huì)自動(dòng)觸發(fā)渲染)
  };

  return (
    <View>
      <Text>當(dāng)前 API 地址:{currentUrl}</Text>
      <Button title="切換 API 地址" onPress={changeUrl} />
    </View>
  );
}

?? 注意:單例變化不會(huì)觸發(fā) React 重渲染,需要手動(dòng)刷新 UI,因此不適合管理驅(qū)動(dòng)界面的狀態(tài)。

2. React Native 內(nèi)置 API

2.1 Dimensions / useWindowDimensions

獲取屏幕尺寸,用于響應(yīng)式布局。

調(diào)用示例:實(shí)時(shí)顯示窗口寬高,旋轉(zhuǎn)屏幕時(shí)自動(dòng)更新

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>當(dāng)前模式:{width > height ? '橫屏' : '豎屏'}</Text>
    </View>
  );
}

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

Dimensions.get('window') 獲取靜態(tài)值,不會(huì)自動(dòng)更新;useWindowDimensions 會(huì)在屏幕旋轉(zhuǎn)或折疊屏變化時(shí)自動(dòng)觸發(fā)重渲染。

2.2 AsyncStorage

持久化存儲(chǔ)鍵值對數(shù)據(jù)(如用戶 token、主題偏好)。

調(diào)用示例:保存用戶昵稱,重啟 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 || '未設(shè)置'}</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

全局事件總線,適合無直接關(guān)系的組件通信,或接收原生模塊事件。

調(diào)用示例:兩個(gè)獨(dú)立組件通過事件傳遞數(shù)據(jù)

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

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

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

  useEffect(() => {
    const subscription = DeviceEventEmitter.addListener('greeting', (data) => {
      setLastMsg(data.message);
    });
    return () => subscription.remove(); // 清理監(jiān)聽器
  }, []);

  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 上下文與狀態(tài)管理

3.1 Props 傳遞

React 基礎(chǔ)通信方式,適合父子直傳。

調(diào)用示例:父組件傳遞數(shù)據(jù)給子組件

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>父組件傳遞數(shù)據(jù)</Text>
      <Child userName={name} />
    </View>
  );
}

多層傳遞會(huì)導(dǎo)致 “props drilling”,維護(hù)困難,此時(shí)應(yīng)使用 Context 或狀態(tài)庫。

3.2 Context API

解決 props 鉆取,適合主題、語言、用戶信息等低頻全局?jǐn)?shù)據(jù)。

調(diào)用示例:全局主題切換,任意子組件消費(fèi)

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

// 1. 創(chuàng)建 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. 消費(fèi) 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 }}>當(dāng)前主題:{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 值變化會(huì)導(dǎo)致所有消費(fèi)者重渲染,高頻變化時(shí)需注意性能。

3.3 第三方狀態(tài)庫(Zustand)

Zustand 提供極簡 API,性能優(yōu)秀,是 RN 社區(qū)熱門選擇。

調(diào)用示例:全局計(jì)數(shù)器,任意組件共享狀態(tài)

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

// 創(chuàng)建 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(適合超大型項(xiàng)目)、Jotai(原子化狀態(tài))。

4. 其他實(shí)用工具

4.1 global 對象

Node.js 風(fēng)格的全局對象,極度不推薦用于業(yè)務(wù)狀態(tài)。僅示例其調(diào)用方式:

// 設(shè)置(污染全局)
global.userId = '123';

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

避免使用,除非調(diào)試或與遺留代碼橋接。

4.2 PixelRatio

獲取設(shè)備像素密度,用于精細(xì)適配。

調(diào)用示例:根據(jù)像素密度調(diào)整圖片尺寸或邊框?qū)挾?/strong>

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; // 最細(xì)線條寬度

  return (
    <View style={styles.container}>
      <Text>設(shè)備像素密度:{scale}</Text>
      <Text>字體縮放比例:{fontScale}</Text>
      <View style={{ borderWidth: onePixel, borderColor: 'red', padding: 10 }}>
        <Text>邊框?qū)挾?= 1 物理像素</Text>
      </View>
    </View>
  );
}

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

5. 快速選擇指南

需求場景 推薦方案 示例章節(jié)
持久化存儲(chǔ)(配置、token) AsyncStorage 2.2
屏幕尺寸響應(yīng) useWindowDimensions 2.1
跨組件事件通知 DeviceEventEmitter 2.3
低頻全局?jǐn)?shù)據(jù)(主題、語言) Context API 3.2
中大型應(yīng)用 UI 狀態(tài) Zustand / Redux Toolkit 3.3
父傳子簡單數(shù)據(jù) Props 3.1
絕對不要做 UI 狀態(tài)管理 單例模式、global 對象 1, 4.1
精細(xì)適配(像素線寬) PixelRatio 4.2

總結(jié)

  • 驅(qū)動(dòng) UI 的狀態(tài) → Zustand(推薦)或 Context(低頻數(shù)據(jù))。
  • 持久化數(shù)據(jù) → AsyncStorage。
  • 跨組件事件 → DeviceEventEmitter。
  • 屏幕適配 → useWindowDimensions。
  • 像素級精細(xì)適配 → PixelRatio。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容