Экран React Native Navigator отображается, когда он не должен - PullRequest
0 голосов
/ 08 апреля 2020

Я использую React Navigation в своем собственном приложении реакции и следит за процессом аутентификации.

app. js отобразит экран аутентификации или домашний экран на основе состояния аутентификации пользователя, называемого userProfile.

userProfile будет передаваться из компонента App в дочерний компонент через контекст React, называемый userContext, и некоторые функции диспетчеризации также будут передаваться как 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>
  );
};

В одном из вложенных дочерних компонентов ProfileScreen под домашним экраном или под MainContainer в приведенном выше коде я пытаюсь использовать userContext для отображения информации о пользователе на экране, и Кнопка «Выйти» использует функцию выхода из диспетчеризации, чтобы обновить состояние аутентификации пользователя до нуля в компоненте приложения.

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, чтобы попросить пользователя выполнить вход еще раз, поскольку в это время мое состояние userProfile в компоненте приложения должно быть нулевым Однако компонент App все еще пытается отобразить ProfileScreen, который выдает ошибку userContext, равную null.

Из моего журнала, после отправки функции выхода в 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 - пустая ошибка

Может ли кто-нибудь помочь мне понять, почему Компонент приложения пытается отобразить profileScreen, когда состояние userProfile равно нулю? И почему profileScreen перерисовывает дважды?

Огромное спасибо

1 Ответ

0 голосов
/ 08 апреля 2020

Похоже, что и ваши условные Stack.Screen s, и ProfileScreen полагаются на состояние userProfile. Поскольку это состояние обновляется асинхронно (как и все в React), это заставляет меня думать, что причиной проблемы является состояние гонки.

Ie, вы отправляете действие для обновления вашего магазина, но ProfileScreen userContext.username получает обновление до того, как защищенный контейнер делает state.userProfile == null ? <Screen1 /> : <Screen2 />.

IMO компонент, который использует данные из внешнего источника, всегда должен защищать себя от пропущенных значений. Особенно, когда вы явно обнуляете это состояние.

В вашем случае я бы просто написал userContext?.username. Или userContext ? <Text>{`Hello ${userContext.username}!`}</Text> : null

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...