问题描述
我有一个简单的 RN 应用程序,到目前为止几乎只有登录功能。它使用 Apollo GraphQL 调用后端并将用户令牌和名称存储在 React Context 对象中。上下文对象具有用户和 setUser 函数(并在 App.js 中实例化)。登录屏幕在成功登录时从 UserContext 对象调用 setUser。
这一直工作正常,但今天我更新到 React 16.3 并开始收到警告:
无法从不同组件的函数体内部更新组件
App.js 使用 useState 来跟踪用户和 setUser 函数并将其放入一个对象中以传递给 UserContext.Provider:
export default function App(props) {
const [user,setUser] = useState(null)
const contextVal = { user: user,setUser: setUser }
return (
<ApolloProvider client={client}>
<UserContext.Provider value={contextVal}>
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<AppNavigator />
</View>
</UserContext.Provider>
</ApolloProvider>
);
}
LoginScreen 从 UserContext 中检索 user 和 setUser,当它从 sigin 突变中获取 data
时,尝试将一个简单的用户对象传递给 setUser:
const [email,setEmail] = useState('')
const [password,setPassword] = useState('')
const [signin,{ data,error,loading }] = useMutation(SIGNIN_MUTATION)
const { user,setUser } = useContext(UserContext)
useEffect(() => {
if (user) {
props.navigation.navigate('Main')
}
},[user])
if (loading) return (
<View style={styles.main}>
<Text style={styles.text}>Logging you in!</Text>
<ActivityIndicator size='large' />
</View>
)
if (data) { // ERROR IS HERE
setUser({
token: data.signin.token,name: data.signin.user.name,})
}
return (
<View style={styles.main}>
<Text style={styles.welcome}>Welcome to Bravauto!</Text>
<Text style={styles.error}>{error ? errorMessage(error) : ''}</Text>
<Text>Please login to continue:</Text>
<TextInput style={styles.input} onChangeText={(text) => setEmail(text)} placeholder="email" />
<TextInput style={styles.input} onChangeText={(text) => setPassword(text)} placeholder="password" secureTextEntry={true} />
<Button title="Login!" onPress={() => signin({ variables: { email: email,password: password } })} />
<Button title="No Account? Register Here" onPress={() => props.navigation.navigate('Registration')} />
</View>
)
我记得要让它工作,我必须在 useEffect 调用中包装“已经有一个用户”的情况(它调用 props.navigate),我发现其他帖子建议将这段代码包装在 useEffect 中将修复警告。但是,如果我将此代码包装在像这样的 useEffect 钩子中:
useEffect(() => {
if (data) {
setUser({
token: data.signin.token,})
}
})
我收到错误而不是警告:
渲染的钩子比预期的少。这可能是由于意外的提前退货声明造成的。
任何帮助将不胜感激!
解决方法
您需要在 useEffect 正在使用的列表中包含数据字段:
useEffect(() => {
if (data) {
setUser({
token: data.signin.token,name: data.signin.user.name,})
}
},[data])
,
哎呀,忘了发帖了,我最终找到了这个问题的答案。解决方案是使用 Apollo 提供的回调:
const [signin,{ loading }] = useMutation(SIGNIN_MUTATION,{
onCompleted: (data) => {
setAuth(data.signin.token);
},onError: (errorMsg) => {
setError(errorMessage(errorMsg));
},errorPolicy: "none"
});
改用这种方法后一切正常。