如何在Redux Persist重hydrate后初始化react-i18next实例?
这个问题我之前也碰到过!核心原因就是你的i18n实例在文件被导入时就立刻执行了初始化,这时候redux-persist还在后台恢复持久化状态,store.getState()根本拿不到缓存的语言设置,自然会 fallback 到设备语言或者默认值。
给你两个靠谱的解决方案,推荐第一个,更优雅:
方案一:利用PersistGate的状态订阅,等Store完全hydrate后再初始化i18n
首先改造你的i18n.js,把初始化逻辑封装成一个异步函数,而不是直接执行:
// i18n.js import i18next from 'i18next'; import { initReactI18next } from 'react-i18next'; import * as RNLocalize from 'react-native-localize'; import store from 'Api/store'; import { setLanguageTag, sincronizeSettings } from 'Api/store/actions'; import { languages } from 'Base/config.json'; const translations = { en: { translation: require('Assets/translations/en.json') }, pt: { translation: require('Assets/translations/pt.json') }, }; const languageDetector = { type: 'languageDetector', async: true, detect: async (callback) => { const storedLanguageTag = store.getState().settings.data?.languageTag; if (storedLanguageTag) { return callback(storedLanguageTag); } const { languageTag: deviceBestAvailableLanguageTag } = RNLocalize.findBestAvailableLanguage( languages.available, ); const currentLanguageTag = deviceBestAvailableLanguageTag || languages.default; store.dispatch(setLanguageTag(currentLanguageTag)); store.dispatch(sincronizeSettings()); callback(currentLanguageTag); }, init: () => {}, cacheUserLanguage: () => {}, }; // 封装初始化函数,返回Promise export const initI18n = async () => { const i18n = i18next.createInstance(); await i18n .use(languageDetector) .use(initReactI18next) .init({ fallbackLng: languages.default, resources: translations, react: { useSuspense: false, }, }); return i18n; };
然后修改你的index.js,通过订阅persistor的状态,等到Store完成hydrate(bootstrapped为true)后再初始化i18n,确保初始化时能拿到缓存的语言:
// index.js(类组件版本) import React, { Component } from 'react'; import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; import store, { persistor } from 'Api/store'; import { initI18n } from 'Api/i18n'; import Launch from './Launch'; import ReduxNetworkProvider from './ReduxNetworkProvider'; import AppContainer from './AppContainer'; export default class App extends Component { state = { i18nReady: false, }; componentDidMount() { // 订阅persistor的状态变化 const unsubscribe = persistor.subscribe(() => { const { bootstrapped } = persistor.getState(); // bootstrapped为true表示Store已经完成hydrate if (bootstrapped) { initI18n().then(() => { this.setState({ i18nReady: true }); unsubscribe(); // 完成后取消订阅,避免重复触发 }); } }); } render() { // 等待i18n初始化完成再渲染主应用 if (!this.state.i18nReady) { return <Launch />; } return ( <Provider store={store}> <PersistGate persistor={persistor} loading={<Launch />}> <ReduxNetworkProvider pingInBackground> <AppContainer /> </ReduxNetworkProvider> </PersistGate> </Provider> ); } }
如果用函数组件的话,写法更简洁:
// index.js(函数组件版本) import React, { useState, useEffect } from 'react'; import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; import store, { persistor } from 'Api/store'; import { initI18n } from 'Api/i18n'; import Launch from './Launch'; import ReduxNetworkProvider from './ReduxNetworkProvider'; import AppContainer from './AppContainer'; export default function App() { const [i18nReady, setI18nReady] = useState(false); useEffect(() => { const unsubscribe = persistor.subscribe(() => { const { bootstrapped } = persistor.getState(); if (bootstrapped) { initI18n().then(() => { setI18nReady(true); unsubscribe(); }); } }); return unsubscribe; }, []); if (!i18nReady) { return <Launch />; } return ( <Provider store={store}> <PersistGate persistor={persistor} loading={<Launch />}> <ReduxNetworkProvider pingInBackground> <AppContainer /> </ReduxNetworkProvider> </PersistGate> </Provider> ); }
方案二:轮询等待Store状态就绪(备选方案)
如果不想大改结构,也可以在languageDetector的detect方法里加一个等待逻辑,直到Store的settings数据加载完成:
// 修改i18n.js里的languageDetector.detect方法 detect: async (callback) => { // 等待Store的settings.data存在,轮询检查 await new Promise(resolve => { const checkSettings = () => { const { data } = store.getState().settings; if (data) { resolve(); } else { setTimeout(checkSettings, 50); // 每50ms检查一次 } }; checkSettings(); }); // 之后的逻辑不变 const storedLanguageTag = store.getState().settings.data?.languageTag; if (storedLanguageTag) { return callback(storedLanguageTag); } const { languageTag: deviceBestAvailableLanguageTag } = RNLocalize.findBestAvailableLanguage( languages.available, ); const currentLanguageTag = deviceBestAvailableLanguageTag || languages.default; store.dispatch(setLanguageTag(currentLanguageTag)); store.dispatch(sincronizeSettings()); callback(currentLanguageTag); },
不过这个方案是轮询,不如方案一利用persistor的订阅机制可靠,更推荐方案一。
核心思路就是让i18n的初始化严格晚于Redux Store的hydrate完成时间,这样就能确保初始化时能拿到持久化的语言配置了。
内容的提问来源于stack exchange,提问作者Tiago Correia
相关产品推荐
相关产品推荐

