в чем причина бесконечных рендеров? - PullRequest
0 голосов
/ 21 июня 2020

Итак, я пытаюсь следовать руководству по реагированию на навигацию для потока аутентификации https://reactnavigation.org/docs/auth-flow/

Но по какой-то причине это вызывает бесконечные повторные рендеры, экран мерцает как сумасшедший, и у меня нет идея, почему кроме отправки в useEffect просто обновляется состояние при каждом рендеринге, но, насколько я понимаю, этого не должно происходить.

import React from "react"
import { NavigationContainer, NavigationContainerRef } from "@react-navigation/native"

import { createNativeStackNavigator } from "react-native-screens/native-stack"
import { PrimaryNavigator, AuthNavigator, DrawerNavigator } from "./primary-navigator"
import { SplashScreen } from "../screens/splash-screen"
import AsyncStorage from "@react-native-community/async-storage"

export type RootParamList = {
  primaryStack: undefined,
  authStack: undefined,
  splashScreen: undefined,
}

const Stack = createNativeStackNavigator<RootParamList>()

const RootStack = () => {
  const AuthContext = React.createContext()

  const [state, dispatch] = React.useReducer(
    (prevState, action) => {
      switch (action.type) {
        case 'RESTORE_TOKEN':
          return {
            ...prevState,
            userToken: action.token,
            isLoading: false,
          }
        case 'SIGN_IN':
          return {
            ...prevState,
            isSignout: false,
            userToken: action.token,
          }
        case 'SIGN_OUT':
          return {
            ...prevState,
            isSignout: true,
            userToken: null,
          }
      }
    },
    {
      isLoading: true,
      isSignout: false,
      userToken: null,
    }
  )

  React.useEffect(() => {
    const bootstrapAsync = async () => {
      let userToken

      try {
        userToken = await AsyncStorage.getItem('userToken')
        console.log(userToken)
      } catch (e) {
        console.log(e)
      }

      // After restoring token, we may need to validate it in production apps

      // This will switch to the App screen or Auth screen and this loading
      // screen will be unmounted and thrown away.

      dispatch({ type: 'RESTORE_TOKEN', token: userToken })
    }

    bootstrapAsync()
  })

  const authContext = React.useMemo(
    () => ({
      signIn: async data => {
        // In a production app, we need to send some data (usually username, password) to server and get a token
        // We will also need to handle errors if sign in failed
        // After getting token, we need to persist the token using `AsyncStorage`
        // In the example, we'll use a dummy token

        dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' })
      },
      signOut: () => dispatch({ type: 'SIGN_OUT' }),
      signUp: async data => {
        // In a production app, we need to send user data to server and get a token
        // We will also need to handle errors if sign up failed
        // After getting token, we need to persist the token using `AsyncStorage`
        // In the example, we'll use a dummy token

        dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' })
      },
    }), [])

  return (
    <AuthContext.Provider value={authContext}>
      <Stack.Navigator
        screenOptions={{
          headerShown: false,
          gestureEnabled: true,

          stackPresentation: "modal",
        }}
      >

        { state.userToken == null
          ? <Stack.Screen
            name="authStack"
            component={AuthNavigator}
            options={{
              headerShown: false
            }}
          />
          : <Stack.Screen
            name="primaryStack"
            component={DrawerNavigator}
            options={{
              headerShown: false,
            }}
          />
        }

      </Stack.Navigator>
    </AuthContext.Provider>
  )
}

export const RootNavigator = React.forwardRef<
  NavigationContainerRef,
  Partial<React.ComponentProps<typeof NavigationContainer>>
>((props, ref) => {
  return (
    <NavigationContainer {...props} ref={ref}>
      <RootStack />
    </NavigationContainer>
  )
})

RootNavigator.displayName = "RootNavigator"

1 Ответ

1 голос
/ 21 июня 2020

Вы пропустили зависимость '[]', которая представлена ​​в учебнике. Когда вы предоставляете зависимость для useEffect, он будет запускаться только при изменении зависимости. Это предусмотрено в учебнике, похоже, вы его пропустили.

Это из учебника

 React.useEffect(() => {
    // Fetch the token from storage then navigate to our appropriate place
    const bootstrapAsync = async () => {
      let userToken;

      try {
        userToken = await AsyncStorage.getItem('userToken');
      } catch (e) {
        // Restoring token failed
      }

      // After restoring token, we may need to validate it in production apps

      // This will switch to the App screen or Auth screen and this loading
      // screen will be unmounted and thrown away.
      dispatch({ type: 'RESTORE_TOKEN', token: userToken });
    };

    bootstrapAsync();
  }, []);
...