【问题标题】:React-Native: cannot update a component while rendering a different componentReact-Native:无法在呈现不同组件时更新组件
【发布时间】:2023-01-25 15:24:55
【问题描述】:

我有这个简单的组件登录:

function Login() {
  const [isFormValidState, setIsFormValidState] = React.useState(false);
  const [credentialState, setCredentialState] = React.useState();

  function getFormErrors(errors: any, dirty: boolean) {
    setIsFormValidState(!Object.keys(errors).length && dirty);
  }

  function getFormValues(values: any) {
    setCredentialState(values);
  }

  function doAction() {
    //credentialState rest call...
  }

  return (
    <View>
      <Text>Login</Text>
      <UserCredentialForm getFormValues={getFormValues} getFormErrors={getFormErrors}/>
      <Button title='Entra' disabled={!isFormValidState} onPress={doAction}/>
    </View>
  );
}

其中调用 UserCredential Form:

export default function UserCredentialForm({ getFormValues, getFormErrors }) {
[...]
  return (
    <Formik innerRef={formRef} validationSchema={formSchema} initialValues={state.form} onSubmit={() => { }}>
      {({ handleChange, values, touched, errors, dirty }) => {
        getFormValues(values);
        getFormErrors(errors, dirty);
        return <React.Fragment>
          // <TextInput/>....              
        </React.Fragment>
      }}
    </Formik>
  );

[...]
}

在我的应用程序中导航时出现此错误:

React Native 在渲染时无法更新组件登录 不同的组件 Formik。

然后它指出了登录组件中getFormValues 处理程序中setCredentialState 中的错误。 我已经使用 ref 而不是状态解决了这个问题,但问题本身对我来说还没有解决。

如果我需要在子事件后更新我的父组件视图怎么办?

【问题讨论】:

标签: javascript reactjs react-native


【解决方案1】:

该错误的原因是因为您在render() 中调用了setState。设置 credentialState 状态的调用 getFormValues(values) 在渲染内部调用。

设置状态后,Login 组件将重新渲染,从而重新创建 getFormValues 的新功能。由于它被用作 UserCredentialForm 的 prop,它还会导致该组件重新渲染,这会导致 Formik 中的 render prop 再次调用,调用 getFormValues 导致状态更改,从而导致无限循环。

您可以尝试的一种解决方案是将 useCallback 添加到这两个函数中,这可以防止它们在状态更改后具有新身份并因此更改道具,从而创建无限重新渲染。

function Login() {
  const [isFormValidState, setIsFormValidState] = React.useState(false);
  const [credentialState, setCredentialState] = React.useState();

  const getFormErrors = useCallback(function getFormErrors(errors: any, dirty: boolean) {
    setIsFormValidState(!Object.keys(errors).length && dirty);
  }, []);

  const getFormValues = useCallback(function getFormValues(values: any) {
    setCredentialState(values);
  }, []);

  function doAction() {
    //credentialState rest call...
  }

  return (
    <View>
      <Text>Login</Text>
      <UserCredentialForm getFormValues={getFormValues} getFormErrors={getFormErrors}/>
      <Button title='Entra' disabled={!isFormValidState} onPress={doAction}/>
    </View>
  );
}

但是,仍然存在一个问题,就是values的身份可能不稳定,将其设置为state,它会一直导致重新渲染。你想要做的是告诉UserCredentialForm即使状态发生变化也不要重新渲染,因为状态在UserCredentialForm中没有用作道具,你可以用React.memo来做到这一点。


export default React.memo(function UserCredentialForm({ getFormValues, getFormErrors }) {
[...]
  return (
    <Formik innerRef={formRef} validationSchema={formSchema} initialValues={state.form} onSubmit={() => { }}>
      {({ handleChange, values, touched, errors, dirty }) => {
        getFormValues(values);
        getFormErrors(errors, dirty);
        return <React.Fragment>
          // <TextInput/>....              
        </React.Fragment>
      }}
    </Formik>
  );

[...]
})

【讨论】:

    【解决方案2】:

    我想你有一个无限循环的渲染,

    您通过 getFormValues 设置状态并且登录组件重新呈现也使 UserCredentialForm 重新呈现,因此它一次又一次地调用 getFormValues

    您可以在 formik 更新值后在 useEffect 挂钩中调用 getFormValues(values)

    【讨论】:

      【解决方案3】:

      您在 Formik 提供的回调中调用 getFormValuesgetFormErrors,这意味着您不能将它们包装在效果挂钩中以抑制此警告,否则它将违反 rules of hooks

      我在 React JS 中遇到了同样的问题,并通过使用以下方法摆脱了它:方法。

      我使用 useFormik 钩子作为替代。

      然后我将 Formik 表单重构为一个新组件,从那里我对父组件进行了状态更改。 这样我既没有违反钩子规则也没有得到这个警告。

      同样在新重构的组件中,您可能需要 useFormikContextuseField

      【讨论】:

        【解决方案4】:

        简单的例子可以像

        用户凭证表格:

         // Formik x React Native example
         import React from 'react';
         import { Button, TextInput, View } from 'react-native';
         import { Formik } from 'formik';
         
         export const MyReactNativeForm = ({onSubmit}) => (
           <Formik
             initialValues={{ email: '' }}
             onSubmit={values => onSubmit(values)}
           >
             {({ handleChange, handleBlur, handleSubmit, values }) => (
               <View>
                 <TextInput
                   onChangeText={handleChange('email')}
                   onBlur={handleBlur('email')}
                   value={values.email}
                 />
                 <Button onPress={handleSubmit} title="Submit" />
               </View>
             )}
           </Formik>
         );
        

        用法像

        function Login() {
        
          function doAction(values) {
            console.log(values);
            //credentialState rest call...
          }
        
          return (
            <View>
              ....
              <UserCredentialForm onSubmit={doAction} />
              ....
            </View>
          );
        }
        

        【讨论】:

        • 对不起,你读过我的问题了吗?这有什么帮助?
        猜你喜欢
        • 1970-01-01
        • 2017-04-23
        • 2020-11-08
        • 1970-01-01
        • 1970-01-01
        • 2017-11-11
        • 1970-01-01
        • 1970-01-01
        • 2021-12-21
        相关资源
        最近更新 更多