【问题标题】:Fix "Cannot update component from inside function body of different component" warning in React Native app using Context使用 Context 修复 React Native 应用程序中的“无法从不同组件的函数体内更新组件”警告
【发布时间】:2021-04-07 11:17:13
【问题描述】:

我有一个简单的 RN 应用程序,到目前为止几乎只有登录。它使用 Apollo GraphQL 调用后端并将用户令牌和名称存储在 React Context 对象中。上下文对象具有用户和 setUser 函数(并在 App.js 中实例化)。登录屏幕在成功登录时从 UserContext 对象调用 setUser。

这一直运行良好,但今天我更新到 React 16.3 并开始收到警告:

无法从不同组件的函数体内更新组件

如果我们收到成功的响应,则列出我调用 setUser 函数的行。

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 中检索用户和 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,
        name: data.signin.user.name,
      })
    }
  })

我收到错误而不是警告:

渲染的钩子比预期的要少。这可能是由于意外的提前退货声明造成的。

任何帮助将不胜感激!

【问题讨论】:

  • 数据变量从何而来?
  • 这是来自 GraphQL 调用返回的数据。在这种情况下,用户名和令牌。

标签: javascript reactjs react-native apollo


【解决方案1】:

您需要将数据字段包含在 useEffect 正在使用的列表中:

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

【讨论】:

  • 啊,是的,忘了说那是我立即尝试的,遵循与上面的“if(user)”useEffect 调用相同的模式。我得到相同的“渲染更少的钩子”错误。我觉得这一定是一个问题,因为 setUser 来自 App.js 中的 useState 调用,但我不知道如何设置上下文状态。 (另一方面,我正在考虑仅将 Apollo 缓存用于我的全局状态并完全装袋 Context。)
【解决方案2】:

哎呀,忘了发布我最终找到了答案。解决方案是使用 Apollo 提供的回调:

  const [signin, { loading }] = useMutation(SIGNIN_MUTATION, {
    onCompleted: (data) => {
      setAuth(data.signin.token);
    },
    onError: (errorMsg) => {
      setError(errorMessage(errorMsg));
    }, 
    errorPolicy: "none"
  });

更改为这种方法后一切正常。

【讨论】:

    猜你喜欢
    • 2022-01-13
    • 1970-01-01
    • 2020-06-16
    • 2020-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-05
    • 2021-02-06
    相关资源
    最近更新 更多