Replies: 24 comments 17 replies
-
I'm afraid there isn't much we can do without a reproduction. There're were no state updates added to the RHF codebase in-between the versions you've mentioned. |
Beta Was this translation helpful? Give feedback.
-
Make sure you don't call |
Beta Was this translation helpful? Give feedback.
-
I've seen this error first time at the moment when I updated react-hook-form so it brought me here. I'll try to find cause of this issue and reproduce it, but my first attempts to debug dist file of your app failed cos I'm not sure which one is imported by vite (hopefully not those minified). |
Beta Was this translation helpful? Give feedback.
-
Here's an example of what I'm talking about: #9632 I understand that you first saw this issue while using RHF. There're quite a few form state / data manipulations you can do with the library's API and a lot of the time it's the user-land calls to those APIs that cause the component update issue like you're facing. |
Beta Was this translation helpful? Give feedback.
-
@Moshyfawn Already bisected line of code which causes it. Now I'm going to find out how to reproduce it, but its called directly from Edit: "Maximum update depth exceeded" was caused by undefined defaultValue on Controller, but it is somehow related to mentioned commit. |
Beta Was this translation helpful? Give feedback.
-
Here is the test code that reproduces this issue (tested on that particular commit from test folder of your project): import React from 'react';
import {
fireEvent,
render,
screen,
waitFor
} from '@testing-library/react';
import { useForm } from '../../useForm';
import { FormProvider, useFormContext } from '../../useFormContext';
describe('formState', () => {
it('should properly set isDirty when disabled is dynamicaly changed', async () => {
const spyError = jest.spyOn(console, 'error');
let isDirty: null | boolean = null;
const defaultValues = { name: 'initial', disableName: true };
const FieldList = () => {
const { formState, register, watch, reset } = useFormContext();
const disableName = watch('disableName');
isDirty = formState.isDirty;
// we haven't form data so we won't render fields
if (disableName === undefined) {
return (
<input
type="button"
onClick={() => reset(defaultValues)}
value="Emulate fetch form values"
/>
);
}
return (
<>
<input type="text" {...register('name', { disabled: disableName })} data-testid="text-input" />
<input type="checkbox" {...register('disableName')} />
</>
);
}
const App = () => {
const methods = useForm();
return (
<FormProvider {...methods}>
<form>
<FieldList />
</form>
</FormProvider>
);
};
render(<App />);
const reset = screen.getByRole('button');
// FAILS
// user didn't touch single field yet so isDirty should be false
// lets skip it to get further
//expect(isDirty).toBe(false);
fireEvent.click(reset);
const checkbox = screen.getByRole('checkbox');
const textInput = screen.getByTestId("text-input");
await waitFor(() => {
expect(checkbox).toBeChecked();
expect(textInput).toBeDisabled();
// FAILS and triggers https://github.com/react-hook-form/react-hook-form/issues/10908
// this spy check should be bind to all tests
expect(spyError).not.toHaveBeenCalled();
// FAILS
// user didn't touch single field yet so isDirty should be false
expect(isDirty).toBe(false);
});
// now user changes value first time
fireEvent.click(checkbox);
// checkbox was clicked and its value differs from defaultValue so isDirty should be true
await waitFor(() => {
expect(checkbox).not.toBeChecked();
expect(textInput).not.toBeDisabled();
expect(isDirty).toBe(true);
});
// text input is enabled and is now editable so we change value
fireEvent.change(textInput, {target: {value: 'foo'}});
// form should be still dirty
await waitFor(() => expect(isDirty).toBe(true));
// user returns checkbox's value to defaultValue which disables textInput
fireEvent.click(checkbox);
// now form should be in initial state
await waitFor(() => {
expect(checkbox).toBeChecked();
expect(textInput).toBeDisabled();
expect(isDirty).toBe(false);
});
});
}); |
Beta Was this translation helpful? Give feedback.
-
I'm experiencing the same problem after updating to .46 from .45. I'll try to provide more info in the following days if I'm able to isolate the source of the problem in my components. |
Beta Was this translation helpful? Give feedback.
-
@ettoredn Warnings appear when disabled fields are registered in other components than where useForm hook is initialized. That's why their examples have empty console - they use single components. I debugged which |
Beta Was this translation helpful? Give feedback.
-
It's not just a warning in my case. It breaks the whole app because of infinite re-rendering loop. |
Beta Was this translation helpful? Give feedback.
-
@bluebill1049 This is serious issue. It was wrongly tagged as question. I wrote even test to reproduce those warnings. I can even write test which crashes whole app if you want. There is issue #10907 related to this one and people are waiting for fix. This is showstopper for updating this lib until resolved. Thanks |
Beta Was this translation helpful? Give feedback.
-
Just updated from rhf 7.45.4 to 7.46.0 and this error is showing up for us too, I first noticed it in tests. This was not an issue in 7.45.4, not saying 7.46 is broken - maybe it's revealing an issue in our approach, idk. But everything was working fine in our app so I'm not sure. Here's the error I get: Warning: Cannot update a component (`TextAreaInputForm`) while rendering a different component (`TextAreaInput`). To locate the bad setState() call inside `TextAreaInput`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
at TextAreaInput (a\path\\app\components\forms\inputs\textarea\textarea.tsx:14:3)
at form
at FormProvider (file:///.../node_modules/react-hook-form/dist/index.esm.mjs:164:13)
at Form (a\path\\app\components\forms\forms.tsx:61:3)
at TextAreaInputForm (a\path\\app\components\forms\inputs\textarea\tests\textarea.spec.tsx:20:3) Here's the test mock form: const TextAreaInputForm = ({
defaultValue,
disabled,
required,
name,
...props
}: TextAreaInputTestProps): JSX.Element => {
const schema = z.object({
[name]: zodTextInputSchema(z.string().min(3), Boolean(required), Boolean(disabled))
})
type Model = z.infer<typeof schema>
const methods = useForm<Model>(schema, {
defaultValues: {
[name]: defaultValue
}
})
return (
<Form<Model> methods={methods} schema={schema}> // our form auto-passing control/register stuff to fields
<TextAreaInput name={name} {...props} />
<SubmitButton text='Submit' />
</Form>
)
} Here's our textarea wrapper, which uses the blueprintjs textarea component: export const TextAreaInput = ({
disabled,
inputClassName,
label,
name,
readOnly,
registerOpts,
required,
...props
}: TextAreaInputProps): JSX.Element => {
const { getFieldState, register, formState } = useFormContext()
const { error, isTouched } = getFieldState(name, formState)
const { isSubmitted } = formState
const { ref, ...reg } = register(name, {
...registerOpts,
disabled,
required
})
const classNames = clsx(formsStyles.textFormField, 'flex-1', 'text-cursor', inputClassName, {
[`${formsStyles.error} small-red-text`]: (isTouched || isSubmitted) && !disabled && !readOnly && error
})
return (
<>
{label && <Label htmlFor={name} label={label} required={required} />}
<TextArea
autoResize={false}
className={classNames}
fill
id={name}
inputRef={ref}
readOnly={readOnly}
{...props}
{...reg}
/>
</>
)
} |
Beta Was this translation helpful? Give feedback.
-
I have the same issue
|
Beta Was this translation helpful? Give feedback.
-
Same issue, narrowed it down to a conditional |
Beta Was this translation helpful? Give feedback.
-
I investigated, it happens if |
Beta Was this translation helpful? Give feedback.
-
If the property inside the second argument object of the Therefore, you should not set the const { ref, ...rest } = register(name as Path<T>, validation?.disabled ? undefined : validation);
...
<input
disabled={validation?.disabled}
{...rest}
/> |
Beta Was this translation helpful? Give feedback.
-
I am having the same issue with the Controller when used in an array of fields via
So, it is a custom FormComponent, which has (among others) an array of PartialEmailForm components, which, in turn, have a custom FormSelect component (representing the email |
Beta Was this translation helpful? Give feedback.
-
Not sure if this will help, when adding conditionally a form field to the dom to a form who's using controller, you can use : form.reset({ ...form.getValues() }) To me it seems that it has resolve the following error : Cannot update a component (X) while rendering a different component (Controller). an example of usage : this codesandbox |
Beta Was this translation helpful? Give feedback.
-
I had the same issue when I was trying to do this inside a component that gets the form from props form.setValue("example", "") Used this instead and the warning went away form.reset() |
Beta Was this translation helpful? Give feedback.
-
For those who are having problems because of working with https://github.com/orgs/react-hook-form/discussions/11941 In addition, it's important to mention that if you don't put the |
Beta Was this translation helpful? Give feedback.
-
I see many workarounds here but not sure there is an appropriate one in case you are using this This is the only reason i am using it inside of The |
Beta Was this translation helpful? Give feedback.
-
in my case i remove the disabled prop from |
Beta Was this translation helpful? Give feedback.
-
Setting the disabled property directly on the input field is fine for the UI but you'll see the disabled input values on submit |
Beta Was this translation helpful? Give feedback.
-
In my case, the issue stems from the disabled={isPending} prop on the form inputs and button. When you disable the form elements, React Hook Form tries to update the form fields while the inputs are disabled, causing the warning. Why the Issue Occurs:
I move |
Beta Was this translation helpful? Give feedback.
-
With shadcn's const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
// See https://github.com/orgs/react-hook-form/discussions/10964#discussioncomment-8481087
const disabled = (props.disabled || undefined) as boolean;
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} disabled={disabled} />
</FormFieldContext.Provider>
);
}; |
Beta Was this translation helpful? Give feedback.
-
Version Number
7.46.1
Codesandbox/Expo snack
https://codesandbox.io/s/react-hook-form-v7-form-context-forked-z59flv
Steps to reproduce
I was not able to reproduce it in sandbox. Partly because I'm not familiar with typescript and party because our app is quite complex and this issue is probably conditional by setup. However I was able to manually bisect version that introduced it, which is 7.46.0 and later (7.45.4 was fine). Stackoverflow thread about this issue says that it is caused by calling setState outside of useEffect. All errors points to either react-hook-form's register method on field or to Controller itself. We are using react 17.0.2.
Expected behaviour
It should not throw any errors.
What browsers are you seeing the problem on?
Chrome
Relevant log output
Code of Conduct
Beta Was this translation helpful? Give feedback.
All reactions