[英]React-Native: cannot update a component while rendering a different component
I've got this simple component Login:我有这个简单的组件登录:
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>
);
}
Which calls UserCredentialForm:其中调用 UserCredentialForm:
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>
);
[...]
}
While navigating in my app I've got this error:在我的应用程序中导航时出现此错误:
react native cannot update a component Login while rendering a different component Formik.
react native 无法在呈现不同组件 Formik 时更新组件登录。
Then it points me to the error in the setCredentialState
inside getFormValues
handler in Login component.然后它指出了登录组件中
getFormValues
处理程序中setCredentialState
中的错误。 I've resolved this using a ref instead of a state, but the problem itself is unsolved to me.我已经使用 ref 而不是 state 解决了这个问题,但问题本身对我来说还没有解决。
What if I need to update my parent component view after a child event?如果我需要在子事件后更新我的父组件视图怎么办?
The reason for that error is because you call setState
inside render()
.该错误的原因是因为您在
render()
中调用了setState
。 The call getFormValues(values)
, which set the state of credentialState
is called inside the render.设置
credentialState
的 state 的调用getFormValues(values)
在渲染内部调用。
When the state is set, the Login
component get rerendered, thus recreating a new function of getFormValues
.设置 state 后,将重新呈现
Login
组件,从而重新创建一个新的 function 的getFormValues
。 As this is used as the prop of UserCredentialForm
, it also causes that component to rerender, which causes the render prop inside Formik
to calls again, which calls getFormValues
causing the state change, causing an infinite loop.由于它被用作
UserCredentialForm
的 prop,它还会导致该组件重新渲染,这会导致Formik
中的 render prop 再次调用,调用getFormValues
导致 state 更改,从而导致无限循环。
One solution you can try is to add useCallback to the two functions, which prevent them to have new identities after the state changes and consequently change the props, thus creating infinite rerender.您可以尝试的一种解决方案是将useCallback添加到这两个函数,这可以防止它们在 state 更改后具有新身份并因此更改道具,从而创建无限重新渲染。
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>
);
}
However, there is still an issue and that is the identity of values
may not be stable and by setting it to state, it will keep causing rerender.但是,仍然存在一个问题,即
values
的标识可能不稳定,将其设置为 state 会导致重新渲染。 What you want to do is to tell UserCredentialForm
not to rerender even when that state changes, and since the state is not used as a prop in UserCredentialForm
, you can do that with React.memo .你想要做的是告诉
UserCredentialForm
即使 state 发生变化也不要重新呈现,并且由于 state 没有用作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>
);
[...]
})
I think you got an unlimited loop of rendering,我想你有一个无限循环的渲染,
you setState by getFormValues and the Login component re-render make UserCredentialForm re-render too, so it call getFormValues again and again您通过 getFormValues 设置状态并且登录组件重新呈现也使 UserCredentialForm 重新呈现,因此它一次又一次地调用 getFormValues
You can call getFormValues(values) in a useEffect hook after values of formik update您可以在 formik 更新值后在 useEffect 挂钩中调用 getFormValues(values)
You are calling getFormValues
and getFormErrors
inside a callback provided by Formik, that means you cannot wrap them inside an effect hook to suppress this warning or it will violate rules of hooks .您在 Formik 提供的回调中调用
getFormValues
和getFormErrors
,这意味着您不能将它们包装在效果钩子中以抑制此警告,否则它将违反hooks 规则。
I faced the same issue in React JS and got rid of it by using the following: approach.我在 React JS 中遇到了同样的问题,并通过使用以下方法摆脱了它:方法。
I used useFormik hook as an alternate.我使用useFormik钩子作为替代。
and afterwards I refactored Formik form into a new component from where I made state changes to parent component.然后我将 Formik 表单重构为一个新组件,从那里我对父组件进行了 state 更改。 This way I neither violated rules of hooks nor got this warning.
这样我既没有违反钩子规则也没有得到这个警告。
Also in the that newly refactored component you might need useFormikContext and useField同样在新重构的组件中,您可能需要useFormikContext和useField
Simple example can be like简单的例子可以像
UserCredentialForm:用户凭证表格:
// 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>
);
Usage like用法像
function Login() {
function doAction(values) {
console.log(values);
//credentialState rest call...
}
return (
<View>
....
<UserCredentialForm onSubmit={doAction} />
....
</View>
);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.