I have a simple RN app that pretty much only has login so far. It uses Apollo GraphQL to call the backend and stores the user token and name in a React Context object. The context object has the user and a setUser function (and is instantiated in App.js). The login screen calls setUser from the UserContext object on a successful login.
This has been working fine but today I updated to React 16.3 and started getting the warning:
Cannot update a component from inside the function body of a different component
And lists the line where I am calling the setUser function if we received a succesful response.
The App.js uses useState to track the user and setUser function and puts that in a object to pass into the 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>
);
}
And the LoginScreen retrieves user and setUser from the UserContext and when it gets data
from the sigin mutation, tries to pass a simple user object to 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>
)
I remember that to get this working I had to wrap the "already have a user" case (where it calls props.navigate) in a useEffect call, and I found other posts suggesting that wrapping this code in useEffect will fix the warning. However if I wrap this code in a useEffect hook like this:
useEffect(() => {
if (data) {
setUser({
token: data.signin.token,
name: data.signin.user.name,
})
}
})
I get an error instead of a warning:
Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
Any help would be much appreciated!