React Native Context,如何在多个嵌套文件和组件之间共享上下文
Posted
技术标签:
【中文标题】React Native Context,如何在多个嵌套文件和组件之间共享上下文【英文标题】:React Native Context, how to share context beetwing multiple nested files and components 【发布时间】: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 可用于文件之间的其他组件?
【问题讨论】:
【参考方案1】:您可以将上下文创建放在单独的文件中
//AuthContext.js
const AuthContext = createContext();
export default AuthContext;
在 app.js 中,您可以简单地导入并使用它
import AuthContext from './AuthContext.js';
你也可以对 login.js 做同样的事情 然后它将按预期工作。
【讨论】:
谢谢!它适用于登录,但如果我对 DrawerContent 执行相同操作,我会收到错误,无效的挂钩调用。 Hooks 只能在函数组件内部调用。 由于错误表明您的抽屉内容是一个类组件,如果可能的话,您可以将其更改为功能组件,或者在下面的问题***.com/questions/54695835/… 的答案中做一个解决方法 这是我的DrawerContent(由App调用的MyHome调用)import AuthContext from '../AuthContext'; const MyDrawer = (props) => const signOut = useContext(AuthContext); return ( <SafeAreaView style= flex: 1 > <View style= height: 150, alignItems: 'center', justifyContent: 'center' > <TouchableOpacity style= marginTop: 20 onPress=() => signOut()> <Text>Logout</Text> </TouchableOpacity> </ScrollView> </SafeAreaView> ); ; export default MyDrawer;
奇怪,它是一个功能组件,所以它应该按预期工作
我在主应用程序中有一个useEffect,可以吗?但是没有useEffect我无法检查用户是否在启动时已经登录,所有功能都在一个传递给AuthContext的useMemo中......也许我有点困惑以上是关于React Native Context,如何在多个嵌套文件和组件之间共享上下文的主要内容,如果未能解决你的问题,请参考以下文章
收到此错误:错误:捆绑失败:错误:无法解析模块`react-native-safe-area-context`
React this.context 在 React Native 0.61 中返回空对象
在 React Native 中使用 Context 和 React Navigation 处理嵌套屏幕上的更新
React (Native) Context API 导致 Stack Navigator (React Navigation 5) 在状态更新后重新渲染