React-Native (expo) 检索初始主题(或任何初始化值)
Posted
技术标签:
【中文标题】React-Native (expo) 检索初始主题(或任何初始化值)【英文标题】:React-Native (expo) Retrieve an Initial Theme (or any initializing value) 【发布时间】:2021-06-10 16:36:27 【问题描述】:我想为我的(expo 增强版)react-native 应用程序引入一个简单的可配置主题,最初的重点是背景颜色。我在实现动态方面取得了一些成功,例如用户可以在应用程序中选择主题/背景颜色,该颜色会影响所有屏幕。我使用了一种相当典型的技术,其中 App.js 将其 (V5) NavigationContainer 及其主题包装在 Context 提供程序中,例如
return (
<SomeProvider>
<NavigationContainer theme=AppDefaultTheme ref=navigationRef>
... stuff including navigators
<NavigationContainer>
</SomeProvider>
)
在组件方面,我们可以使用 useTheme 挂钩检索主题(最初是默认主题),然后更改背景或其他任何内容,并(暂时)将变异的主题持久保存在 Provider 的 reducer 中。 reducer 还可以将变异的主题持久化到 AsyncStorage 之类的东西中,以便下次应用程序启动。
问题出在应用重启。
似乎没有办法及时从 App.js 中的冷存储(AsyncStorage 或其他)中检索当前主题以将其提供给 NavigationContainer
(记住这一点)
<NavigationContainer theme=AppDefaultTheme ref=navigationRef>
许多帖子指出在 RN/expo 中 没有 beforeRender 钩子或功能。当我们在 useEffect 挂钩中拉回 AppDefaultTheme 时,App.js NavigationContainer 早已离开了站点。
我猜想检索初始状态是不可能的,除非可能使用像 Redux 这样的外部状态管理器。 Redux 人是怎么解决这个问题的?我对“nonReduxLight”非常满意,例如redux 模式使用上述 Provider 包装器,颜色主题功能还不足以让我潜入那个深游泳池,但它已经接近了。
Redux 会做我想做的事吗?有没有更简单的方法?
【问题讨论】:
【参考方案1】:很高兴有兴趣。现在我会回答我自己的问题,因为研究成果丰硕。
Expo 提供 AppLoading 包,该包似乎“保持”初始启动屏幕,同时执行其他通常异步操作。
这会启用以下模式:
-
App.js(或您的初始组件)根据应用程序状态在渲染/返回方法中调用 AppLoading。状态不需要绑定到上下文,因此在创建上下文之前它可以在 App.js 中正常工作。
AppLoading 阻止组件在初始屏幕上渲染,并调用包含渲染前要完成的工作的函数。
在调用的函数中执行您需要执行的任何操作来初始化应用程序。在此特定示例中,我们希望在 App.js 访问应用程序并将其包装在上下文中之前,从冷存储中检索可修改的主题。被调用函数中的水合操作通常是异步的,但我认为不需要。
当承诺兑现时,例如您已经为从网络或冷存储中获取的任何内容添加了水分,翻转了使初始屏幕处于悬念状态的状态,这允许进一步渲染,并消耗您添加的结果。在这种特殊情况下,使用检索到的主题来初始化导航器
我在 Expo 40 中使用它,这种模式效果很好。从架构上讲,它是一个很好的模式......它在生命周期中插入了一个“在渲染任何东西之前”,它不如没有的“在渲染这个组件之前”事件那么好,但对于执行前水合的目的,它是完美的。
它完全不依赖于上下文,所以它可以在调用上下文来包装应用程序之前在 App.js 中使用。
几条笔记,然后是一些例子:
-
我只在 Expo 中测试过,但组件说明表明它可以
通过导入初始依赖项或在 vanilla react-native 中使用
两个
package documentation and example 很棒,但指的是基于类的组件。如果您像我一样喜欢功能组件,这里有一个很棒的functional component example。
AppLoading 组件似乎是 Expo SplashScreen 组件的便捷包装器,它添加了一些我现在不需要的额外功能。你可能会。
令人难以置信的酷...可用于多个组件。例如,您可能有一个加载主题的组件,根据我们的主要用例可能在 App.js 中。根据本文的大部分内容,App.js 工作不需要访问 Context,实际上必须在创建上下文之前进行。
但您可能有另一个组件需要从 Web 或冷存储加载一些首选项,并且它确实需要访问 Context。没问题。将 AppLoading 的另一个实现放在第二个组件中并水合。
不要混淆...... 两个 AppLoading 调用都在等待同一组事件以释放初始屏幕,因此这不是解决“渲染前禁止”一般问题的方法.但它看起来确实是解决“渲染前任何东西问题的解决方案。
最后一个......似乎有很多使用 Redux 进行水合的解决方案来解决这个问题——而且它们可能都很好。然而,这种情况并没有上升到重写应用程序的程度,该应用程序大量且愉快地使用 Context hooks 而不是 Redux。如果我曾经使用 Redux 学习和重写,我可能会在那里补充水分。
好的,废话不多说。这是模式的基础知识。 getColorTheme 是一种自定义方法,可以执行任何所需的操作来获取冷存储数据。
在 App.js 或任何首先加载的内容中:
import React, useState from 'react';
import AppLoading from 'expo-app-loading';
... whatever else you need
function App()
// This bit of component state controls whether apploading is finished or not
const [isReady, setReady] = useState(false);
// This piece of component state holds the hydrated theme to pass to navigation
const [colorTheme, setColorTheme] = useState();
...
// Invoked while the splash screen is showed. Initialize resources here
const _cacheResourcesAsync = async () =>
const storedColorTheme = await getColorTheme(); // an app specific method to fetch theme from cold storage
setColorTheme(storedColorTheme) // now stash the theme in state for retrieval in render
return;
and eventually render and invoke our method using AppLoading:
return (
// Carry out initializing actions, then release the splash screen
isReady === false ? (<AppLoading
startAsync=_cacheResourcesAsync
onFinish=() => setReady(true)
onError=console.warn
/>) :
<MyContextProvider>
<NavigationContainer theme=colorTheme ...>
【讨论】:
以上是关于React-Native (expo) 检索初始主题(或任何初始化值)的主要内容,如果未能解决你的问题,请参考以下文章
await 是 Expo XDE 中 React Native 的保留字错误