Welcome to MLink Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
251 views
in Technique[技术] by (71.8m points)

javascript - Fix "Cannot update component from inside function body of different component" warning in React Native app using Context

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!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You will want to include the data field in the list of ones the useEffect is using:

useEffect(() => {
    if (data) {
      setUser({
        token: data.signin.token,
        name: data.signin.user.name,
      })
    }
  }, [data])

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to MLink Developer Q&A Community for programmer and developer-Open, Learning and Share
...