react-hook-form: Validation does not work correctly on Material-ui input with key prop
Describe the bug I am using material-ui input element. And application has multiple language. I change label prop on language change to pass new label. And i use key prop to trigger styles calculation. But after this, the form validations does not work correctly
Expected behavior Validations should work fine.
Screenshots
Initial Render: No error and submit works fine

After language switch: Error does not go away
Desktop (please complete the following information):
- OS: ios
- Browser: Chrome
Additional context This is how my textfield from material-ui looks:
<TextField name="email" key={state.lang} label={${intl.formatMessage({ id: 'generic.email' })} *} margin="normal" variant="outlined" helperText={errors.email ? intl.formatMessage({ id: errors.email.message }) : ''} inputRef={register} error={errors.email ? true : false} />
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 26 (19 by maintainers)
@bluebill1049 I have the same issue while using Material-UI with react-form-hook. After debugging, I found that the issue is a side effect from method findRemovedFieldAndRemoveListener().
For example, in sample codesandbox of @ASHFAQPATWARI, when switching language, Login component will be re-rendered and method registerIntoFieldRef will be triggered too.’
https://github.com/bluebill1049/react-hook-form/blob/88e29b696f265ffdafd329e038dc0842f75a9dd2/src/useForm.ts#L379
But at this time, fieldsRef already has value (referenced to old HTML Input element of email and password) therefore fieldsRef is not updated.
Right after the execution of registerIntoFieldRef(), JSS library (used by Material-UI) is executed to update style for input component but it also triggers callback on MutationObserver from method onDomRemove().
https://github.com/bluebill1049/react-hook-form/blob/681fd2f824f116dd749075ce42d01f04a7e009b5/src/utils/onDomRemove.ts#L3
Because the HTML Input element from parameter of onDomRemove() is the old element that does not exist in the current rendering page, method isDetached() returns true and onDetachCallback() will be triggered and this callback is findRemovedFieldAndRemoveListener().
https://github.com/bluebill1049/react-hook-form/blob/681fd2f824f116dd749075ce42d01f04a7e009b5/src/utils/onDomRemove.ts#L19
At this time, findRemovedFieldAndRemoveListener() will remove all values in fieldsRef.
https://github.com/bluebill1049/react-hook-form/blob/681fd2f824f116dd749075ce42d01f04a7e009b5/src/logic/findRemovedFieldAndRemoveListener.ts#L5
Now all values in fieldsRef has been removed, when clicking on submit button, method handleSubmit() always gets empty values from method getFieldsValues() and causes the wrong validation.
https://github.com/bluebill1049/react-hook-form/blob/681fd2f824f116dd749075ce42d01f04a7e009b5/src/useForm.ts#L537
In summary, the root cause is from findRemovedFieldAndRemoveListener() which is executed on old data by MutationObserver on document when style is changed.
A workaround is to put unSubscribe() inside Login component to clean up old data before re-rendering.
Updated working sample: https://codesandbox.io/s/react-hook-form-issue-on-first-click-ffhr0
@bluebill1049 Nice trick to update ref 👍 Actually, we can just use “lang” of Login component props as dependency for useEffect.
Ok here is a solution for “hacking”
keyvalue withmaterial-uiProblem: (thanks @leapful based on your investigation)
keyvalue on the same componentregistergets invoked with oldrefthen React work out tree diff, remove the old node and insert the new one. Howeverregisteris always called before that (when new DOM inserted) gets happened not after, henceregisteredref lead to be the deleted one and empty.Solution
called hidden API
unsubscribeon each render, which was suggested by @leapful it works, but terrible from the performance point of view, also his hidden API have been removed in the newest version.manually trigger a re-render after context update to make sure
reigsterget the correct ref after React reconciliation with the diffing, remove and insert. https://codesandbox.io/s/react-hook-form-issue-on-first-click-uxvhxConclusion
ideally,
material-uishould fix this bug on the context API, and should stop users from togglingkeyon the same component to working around the re-render issue. This is definitely not standard to togglekeyon the same component at least from my exp.wow thanks, @leapful for the detailed investigation! I would love to get this working as well.
However, toggling individual element with the
keyis definitely not standard (edge case).I will be looking into this issue see if I can work it out. Feel free to send a PR if you work out a solution too. much appreciated your investigation.
I think the problem is when
keychanged, React willdetecta tree mismatch and removed the old element and inject with a new element. however, the input name still remains the same which leadregistermethod believe it’s the same input, hence the issue.(By the way, for the new version I have already removed
unSubscribefrom public api, so probably shouldn’t rely on this API)really good investigation tho! 🙏 I didn’t even discover that.
(I may have a solution, going to try it out)
I don’t believe the
keyprop is the issue here. I’m able to reproduce it with the codesandbox provided. I’m going to see if I can figure out what is going on.Debugging:
After changing language and the initial submit doesn’t trigger the
getFieldValuebut the second submit does. When thevalidateSchemafunction is called the first time, thedatavalue is{}. Thedatavalue is empty due to the form not seeing any fields in thefieldsRefstate. It looks like the refs are old. When inspecting the refs after language change, it doesn’t highlight the input. It happens after switching language, even switching fromarback toenand triggering submit. I think it may have to do with how material ui is handling the input ref. Something with how Material UI is handling their input ref is essentially causing us to have out of date refs. You can see theelementRefcome intoregisterIntoFieldsRefcorrectly. However, It is doing a check if a ref with the same name already exists in it. So it is getting the previous ref.@bluebill1049 I’m not sure if this is an issue with our lib, Material UI, or both? I suppose we could just overwrite the ref if it is different from the previous?
@bluebill1049 the
keyprop is used by material-ui to recalculate the inline styles as the label width change because the width of text is variable. There is nothing unusual with this as this is the recommended way by material-ui.If I remove the key prop, then styling of my component will not work correctly.
ha, I find the issue. you didn’t give the button a
type="button".button without a type will automatically be assigned as
submit👍