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

(记住这一点) &lt;NavigationContainer theme=AppDefaultTheme ref=navigationRef&gt;

许多帖子指出在 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 的保留字错误

主目录之外的React-Native导入文件

使用 react-native (expo) 切换手电筒

React-native refs 和 Expo Camera

不使用 react-native 的 Expo 分支

expo 应用程序中的 React-native 安装问题