function AppHome({colorMode, currentLang, restartApp}) {
const default_scheme = useColorScheme()
let [color_choice, setColor_choice] = useState(colorMode)
let [lang, setLang] = useState(currentLang)
const colors = {
auto: color_choice === "auto",
...(color_choice === "light" || color_choice === "auto" && default_scheme === "light") ? LightColors : DarkColors
}
const Home = ({navigation, route}) => (
<HomePage
navigation={navigation}
openPremium={() => {}}
NavigationBar={() => {}}
restartApp={restartApp}
/>
)
return (
<LanguageContext.Provider value={{lang, setLang}} >
<ThemeContext.Provider value={colors} >
<NavigationContainer>
<Tab.Navigator screenOptions={{
headerShown: false,
tabBarStyle: {display: 'none'},
}} >
<Tab.Screen name={'Home'} component={Home} />
</Tab.Navigator>
</NavigationContainer>
</ThemeContext.Provider>
</LanguageContext.Provider>
);
}
在此代码中,当我通过编辑设备主题或任何其他状态来更新
default_scheme
时,会触发 NavigationContainer 的重新渲染,这很好。但是,当发生这种情况时,选项卡屏幕会完全刷新,并且我所在的页面会丢失所有进度(例如我在文本输入上写的内容)。我怎样才能防止这种影响?
如果我检查 Home 组件并添加此代码
useEffect(() => {
console.log("This log should appear ONCE.")
}, [])
当我之前谈到的每次刷新被触发时,都会出现日志。
您有一个
Home
的组件定义在另一个组件定义(AppHome
的定义)中实例化。这意味着该组件不是引用稳定的,任何 AppHome
的重新渲染都将完全重新创建 Home
组件,该组件将被完全卸载并重新安装,从而丢失该过程中的所有状态。
但是,我看到您希望这样做是因为
Home
需要访问restartApp
,这是在Home
的上下文中。
选项A
使
Home
成为第一类(参考稳定)组件,并使用 restartApp
的 children
而不是
Tab.Screen
属性将 component
传递给属性。
function Home({navigation, restartApp}) {
return (
<HomePage
navigation={navigation}
openPremium={() => {}}
NavigationBar={() => {}}
restartApp={restartApp}
/>
)
}
function AppHome({colorMode, currentLang, restartApp}) {
const default_scheme = useColorScheme()
let [color_choice, setColor_choice] = useState(colorMode)
let [lang, setLang] = useState(currentLang)
const colors = {
auto: color_choice === "auto",
...(color_choice === "light" || color_choice === "auto" && default_scheme === "light") ? LightColors : DarkColors
}
return (
<LanguageContext.Provider value={{lang, setLang}} >
<ThemeContext.Provider value={colors} >
<NavigationContainer>
<Tab.Navigator screenOptions={{
headerShown: false,
tabBarStyle: {display: 'none'},
}} >
<Tab.Screen name={'Home'}>
{(props) => <Home {...props} restartApp={restartApp} />}
</Tab.Screen>
</Tab.Navigator>
</NavigationContainer>
</ThemeContext.Provider>
</LanguageContext.Provider>
);
}
选项B
出于性能原因,建议使用上下文而不是上述技巧。文档指出(关于使用
children
):
默认情况下,React Navigation 对屏幕组件进行优化,以防止不必要的渲染。使用渲染回调可以消除这些优化。因此,如果您使用渲染回调,则需要确保对屏幕组件使用 React.memo 或 React.PureComponent 以避免性能问题。
我将避免讨论具体细节,因为创建一个上下文并在其中放入一些东西(如
restartApp
),然后将其从子组件中拉出(如Home
)是一项常见任务。
您仍然会将
Home
作为一个单独的组件进行分解,但您可以像以前一样使用 component
上的 Tab.Screen
属性来引用它。 Home
将从您创建的包装 restartApp
的新上下文中获得 NavigationContainer
。