【问题标题】:React Native Context, how to share context beetwing multiple nested files and componentsReact Native Context,如何在多个嵌套文件和组件之间共享上下文
【发布时间】:2020-11-08 19:31:57
【问题描述】:

我对本机反应很陌生,而且我坚持在不同文件中的组件之间传递上下文 基本上我正在按照反应导航 auth-flow https://reactnavigation.org/docs/auth-flow/ 构建登录流程 我的场景如下:

在 App.js 中 带有 Login/Register/Home 的堆栈屏幕,根据登录状态显示 Login/Register 或 Home 主屏幕由一个抽屉组件组成,使用自定义抽屉和两个组件(主页和关于)

//VARIOUS IMPORT    
const Drawer = createDrawerNavigator();

const HeaderOption = () => ({
  headerShown: false,
  // animationTypeForReplace: state.isSignout ? 'pop' : 'push',
});

const AppStack = createStackNavigator();

const AuthContext = createContext();

//THE DRAWER FOR HOME
function DrawerNavigator(props) {
  return (
    <Drawer.Navigator
      initialRouteName="Home"
      drawerContent={(props) => MyDrawer(props)}
    >
      <Drawer.Screen name="Home" component={Home} />
      <Drawer.Screen name="About" component={About} />
    </Drawer.Navigator>
  );
}
//MAIN APP
export default function App({ navigation }) {
  const [state, dispatch] = 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,
    }
  );

  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) {
      }

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

    bootstrapAsync();
  }, []);

  const authContext = useMemo(
    () => ({
      signIn: async (data) => {
        // LOGIN PROCEDURE

        dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
      },
      signOut: () => dispatch({ type: 'SIGN_OUT' }),
      signUp: async (data) => {
        // SUBSCRIBE PROCEDURE

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

  if (state.isLoading) {
    // We haven't finished checking for the token yet
    return (
      <View>
        <Text>Loading</Text>
      </View>
    );
  }
  return (
    <AuthContext.Provider value={authContext}>
      <NavigationContainer>
        <AppStack.Navigator initialRouteName="Login">
          {state.userToken == null ? (
            <>
              <AppStack.Screen
                name="Login"
                component={LoginScreen}
                options={HeaderOption}
              />
              <AppStack.Screen
                name="Register"
                component={RegisterScreen}
                options={HeaderOption}
              />
            </>
          ) : (
            <AppStack.Screen
              name="HomeApp"
              component={DrawerNavigator}
              options={HeaderOption}
            />
          )}
        </AppStack.Navigator>
      </NavigationContainer>
    </AuthContext.Provider>
  );
}

在 LoginScreen.js 中 有效的登录屏幕(如果未登录,则会在应用启动时显示)

//import

export default function LoginScreen(props) {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const { signIn } = useContext(AuthContext);

  return (
    <View
      style={{
        flex: 1,

        backgroundColor: Constants.MAIN_GREEN,
      }}
    >
      <View style={{ ...styles.container }}>
        <StatusBar hidden={true} />
        <View style={{ ...styles.logoContainer }}>
          <Image
            style={styles.logoIcon}
            source={require('../assets/logo_popeating_amp.png')}
          />
        </View>

        <View style={{ ...styles.inputContainer }}>
          <Image
            style={styles.inputIcon}
            source={require('../assets/mail.png')}
          />
          <TextInput
            autoFocus={true}
            placeholder="Email address"
            onChangeText={(email) => setEmail(email)}
            value={email}
            label="Email"
            style={styles.inputs}
            keyboardType={'email-address'}
          />
        </View>
        <View style={{ ...styles.inputContainer }}>
          <Image
            style={styles.inputIcon}
            source={require('../assets/password.png')}
          />
          <TextInput
            placeholder="Password"
            onChangeText={(password) => setPassword(password)}
            value={password}
            secureTextEntry={true}
            label="Password"
            style={styles.inputs}
          />
        </View>
        <TouchableHighlight
          style={[styles.buttonContainer, styles.loginButton]}
          onPress={() => signIn({ email, password })}
          underlayColor={Constants.HI_COLOR}
        >
          <Text style={styles.loginText}>LOGIN</Text>
        </TouchableHighlight>

        <TouchableHighlight
          style={styles.buttonContainer}
          onPress={() => props.navigation.navigate('HomeApp')}
          underlayColor={Constants.HI_COLOR}
        >
          <Text>Forgot your password?</Text>
        </TouchableHighlight>

        <TouchableHighlight
          style={styles.buttonContainer}
          onPress={() => props.navigation.navigate('Register')}
          underlayColor={Constants.HI_COLOR}
        >
          <Text>Register</Text>
        </TouchableHighlight>
      </View>
    </View>
  );
}



const styles = StyleSheet.create({
  //styles
});

在 DrawerContent.js 中 主页的抽屉,其中包含指向主页的链接、指向关于的链接、指向注销的链接

在 Home.js 中 主页面是Drawer的初始路由

每次我尝试启动应用程序时 错误是 未处理的承诺拒绝:ReferenceError:找不到变量:AuthContext

似乎 LoginScreen 无法访问 AuthContext,我怎样才能让 AuthContext 可用于文件之间的其他组件?

【问题讨论】:

    标签: react-native react-native-navigation react-context


    【解决方案1】:

    您可以将上下文创建放在单独的文件中

    //AuthContext.js
    const AuthContext = createContext();
    export default AuthContext;
    

    在 app.js 中,您可以简单地导入并使用它

    import AuthContext from './AuthContext.js';
    

    你也可以对 login.js 做同样的事情 然后它将按预期工作。

    【讨论】:

    • 谢谢!它适用于登录,但如果我对 DrawerContent 执行相同操作,我会收到错误,无效的挂钩调用。 Hooks 只能在函数组件内部调用。
    • 由于错误表明您的抽屉内容是一个类组件,如果可能的话,您可以将其更改为功能组件,或者在下面的问题stackoverflow.com/questions/54695835/… 的答案中做一个解决方法
    • 这是我的DrawerContent(由App调用的MyHome调用)import AuthContext from '../AuthContext'; const MyDrawer = (props) =&gt; { const { signOut } = useContext(AuthContext); return ( &lt;SafeAreaView style={{ flex: 1 }}&gt; &lt;View style={{ height: 150, alignItems: 'center', justifyContent: 'center' }} &gt; &lt;TouchableOpacity style={{ marginTop: 20 }} onPress={() =&gt; signOut()}&gt; &lt;Text&gt;Logout&lt;/Text&gt; &lt;/TouchableOpacity&gt; &lt;/ScrollView&gt; &lt;/SafeAreaView&gt; ); }; export default MyDrawer;
    • 奇怪,它是一个功能组件,所以它应该按预期工作
    • 我在主应用程序中有一个useEffect,可以吗?但是没有useEffect我无法检查用户是否在启动时已经登录,所有功能都在一个传递给AuthContext的useMemo中......也许我有点困惑
    猜你喜欢
    • 2016-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-01
    • 2020-09-22
    • 1970-01-01
    • 2018-01-18
    • 1970-01-01
    相关资源
    最近更新 更多