首页 > 解决方案 > 不应该渲染的 React Native Navigator 屏幕

问题描述

我在我的 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 组件中将 state 设置为 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 会重新渲染两次?

非常感谢你

标签: javascriptreactjsreact-nativereact-navigation

解决方案


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

即,您调度操作以更新您的商店,但 ProfileScreenuserContext.username在受保护容器接收更新之前接收到更新state.userProfile == null ? <Screen1 /> : <Screen2 />

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

在你的情况下,我只会写userContext?.username。或者userContext ? <Text>{`Hello ${userContext.username}!`}</Text> : null


推荐阅读