不应该渲染的 React Native Navigator 屏幕

Posted

技术标签:

【中文标题】不应该渲染的 React Native Navigator 屏幕【英文标题】:React Native Navigator screen rendered when it's not supposed to 【发布时间】:2020-07-20 09:31:49 【问题描述】:

我在我的 react 本机应用程序上使用 React Navigation 并遵循其身份验证流程。

app.js 将根据称为 userProfile 的用户身份验证状态呈现身份验证屏幕或主屏幕。

userProfile 将通过称为 userContext 的 React 上下文从 App 组件传递到子组件,并且一些调度函数也将被称为 authContext 传递。

const App: () => React$Node = () => 

  const [state, dispatch] = React.useReducer(
    (prevState, action) => 
      switch (action.type) 
        case SIGN_IN:
          return 
            ...prevState,
            userProfile: action.userProfile,
          ;
        case SIGN_OUT:
          return 
            ...prevState,
            userProfile: null,
          ;
      
    ,
    
      isLoading: true,
      userProfile: null,
    
  )


  React.useEffect(() => 
    isMountedRef.current = true;
    return () => (isMountedRef.current = false);
  , []);


  const authContext = React.useMemo(
    () => (
      signIn: async userProfile => 
        try 
          await AsyncStorage.setItem('userProfile', JSON.stringify(userProfile))
          dispatch( type: SIGN_IN, userProfile )
         catch (error) 
          console.log(error)
        
      ,
      signOut: async () => 
        try 
          await AsyncStorage.removeItem('userProfile');
          dispatch( type: SIGN_OUT )
         catch (error) 
          console.log(error)
        
      
    ),
    []
  );

  return (
    <AuthContext.Provider value=authContext, userContext: state.userProfile>
      <NavigationContainer ref=navigationRef>
        <Stack.Navigator>
          console.log('app render: ', state.userProfile)
          
            state.userProfile == null ?
              (<Stack.Screen name="AuthContainer" component=AuthContainer options= headerShown: false  />) :
              (<Stack.Screen name="MainContainer" component=MainContainer options= headerShown: false  />)
          
        </Stack.Navigator>
        
      </NavigationContainer>
    </AuthContext.Provider>
  );
;

在上面代码中主屏幕或 MainContainer 下的嵌套子组件 ProfileScreen 之一中,我正在尝试使用 userContext 在屏幕上显示用户信息,并且注销按钮正在使用调度注销功能进行更新App 组件中的用户身份验证状态为 null。

function ProfileScreen() 
    const  authContext, userContext  = React.useContext(AuthContext)
    console.log('profile render: ', userContext)
    return (
        <View style= flex: 1, alignItems: 'center', justifyContent: 'center' >
            <Text>Hello userContext.username!</Text>
            <Text>Profile Screen</Text>
            <Button
                title="Sign Out"
                onPress=() => authContext.signOut()
            />
        </View>
    );


export default ProfileScreen;

调度退出功能后,我希望应用导航到身份验证屏幕 AuthContainer 以要求用户再次登录,因为此时我在 App 组件中的 userProfile 状态应该为空。但是,App 组件仍在尝试呈现抛出错误 userContext is null 的 ProfileScreen。

从我的日志中,我在 ProfileScreen 中调度退出功能后,它显示

app render: null     ---> App re-render
profile render: null   ---> profileScreen re-render
profile render: null   ---> profileScreen re-render
auth container   ---> finally start rendering Auth Screen

然后立即抛出 userContext is null 错误

谁能帮助我理解为什么 App 组件在 userProfile 状态为空时尝试渲染 profileScreen?为什么 profileScreen 会重新渲染两次?

谢谢你

【问题讨论】:

【参考方案1】:

看起来您的条件 Stack.Screens 和 ProfileScreen 都依赖于 userProfile 状态。因为该状态是异步更新的(就像 React 中的所有东西一样),它让我相信竞争条件导致了你的问题。

即,您调度操作以更新您的商店,但 ProfileScreen userContext.username 在受保护容器执行 state.userProfile == null ? &lt;Screen1 /&gt; : &lt;Screen2 /&gt; 之前收到更新。

IMO 使用外部数据源的组件必须始终保护自己免受缺失值的影响。尤其是当您明确取消该状态时。

在你的情况下,我只写userContext?.username。 或userContext ? &lt;Text&gt;`Hello $userContext.username!`&lt;/Text&gt; : null

【讨论】:

感谢您回答我的问题。我认为 userProfile 是否正在异步更新并不重要。当 userProfile 状态改变时会发生重新渲染。我知道通过在使用它的属性之前检查 userContext 是一个简单的修复,我只是对为什么我的 App 组件没有根据条件正确呈现感兴趣。 您好科林,感谢您的回复。我认为您假设您的 Stack.Screen 卸载 first ,因此 ProfileScreen 将永远不会收到 null 配置文件状态。根据定义,异步事件的完成是无序的。 React 似乎完全按照这里的预期工作。

以上是关于不应该渲染的 React Native Navigator 屏幕的主要内容,如果未能解决你的问题,请参考以下文章

React Native 条件渲染不起作用

React Native:如何更改 onPress 事件的视图

React Native:播放视频错误组件异常

React Native 之createDrawerNavigator和createSwitchNavigator

React Native - FlatList 不渲染

为啥组件在状态更改后不重新渲染。在 react-native 函数组件中