在 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。