From dd821f6a5e067b04e8df8cd2537cfd6f1e612433 Mon Sep 17 00:00:00 2001 From: Bill Date: Fri, 21 May 2021 18:23:02 +1000 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9E=20fix=20#5276=20setValue=20with=20?= =?UTF-8?q?null=20(#5278)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix #5276 set null bug * include unit test update --- src/__tests__/useForm/setValue.test.tsx | 80 +++++++++++++++++ src/logic/getFieldsValues.ts | 3 +- src/useForm.ts | 114 +++++++++++++----------- 3 files changed, 145 insertions(+), 52 deletions(-) diff --git a/src/__tests__/useForm/setValue.test.tsx b/src/__tests__/useForm/setValue.test.tsx index 0ebee7e4202..2664833eaf6 100644 --- a/src/__tests__/useForm/setValue.test.tsx +++ b/src/__tests__/useForm/setValue.test.tsx @@ -939,4 +939,84 @@ describe('setValue', () => { fireEvent.click(screen.getByRole('button')); expect(fields).toMatchSnapshot(); }); + + describe('when set field to null', () => { + it('should be able to set correctly with register', () => { + let result: unknown; + + type FormData = { + user: { name: string } | null; + }; + + function App() { + const { setValue, watch, register } = useForm({ + defaultValues: { + user: { + name: 'John Doe', + }, + }, + }); + + result = watch(); + + register('user'); + + return ( +
+ +
+ ); + } + + render(); + + actComponent(() => { + fireEvent.click(screen.getByRole('button')); + }); + + expect(result).toEqual({ + user: null, + }); + }); + + it('should be able to set correctly without register', () => { + let result: unknown; + + type FormData = { + user: { name: string } | null; + }; + + function App() { + const { setValue, watch } = useForm({ + defaultValues: { + user: { + name: 'John Doe', + }, + }, + }); + + result = watch(); + + return ( +
+ +
+ ); + } + + render(); + + actComponent(() => { + fireEvent.click(screen.getByRole('button')); + }); + + expect(result).toEqual({ + user: null, + }); + }); + }); }); diff --git a/src/logic/getFieldsValues.ts b/src/logic/getFieldsValues.ts index 4074efc6225..f2154cb5c1e 100644 --- a/src/logic/getFieldsValues.ts +++ b/src/logic/getFieldsValues.ts @@ -1,6 +1,7 @@ import * as React from 'react'; import { FieldRefs, FieldValues } from '../types'; +import isNullOrUndefined from '../utils/isNullOrUndefined'; import omit from '../utils/omit'; import set from '../utils/set'; @@ -12,7 +13,7 @@ const getFieldsValues = ( for (const name in fieldsRef.current) { const field = fieldsRef.current[name]; - if (field) { + if (field && !isNullOrUndefined(output)) { const _f = field._f; const current = omit(field, '_f'); diff --git a/src/useForm.ts b/src/useForm.ts index 60667c192e3..b1544f53b25 100644 --- a/src/useForm.ts +++ b/src/useForm.ts @@ -245,58 +245,70 @@ export function useForm< shouldRegister?: boolean, ) => { shouldRegister && register(name as Path); - const _f = get(fieldsRef.current, name, {})._f as Field['_f']; - - if (_f) { - const value = - isWeb && isHTMLElement(_f.ref) && isNullOrUndefined(rawValue) - ? '' - : rawValue; - _f.value = getFieldValueAs(rawValue, _f); - - if (isRadioInput(_f.ref)) { - (_f.refs || []).forEach( - (radioRef: HTMLInputElement) => - (radioRef.checked = radioRef.value === value), - ); - } else if (isFileInput(_f.ref) && !isString(value)) { - _f.ref.files = value as FileList; - } else if (isMultipleSelect(_f.ref)) { - [..._f.ref.options].forEach( - (selectRef) => - (selectRef.selected = (value as string[]).includes( - selectRef.value, - )), - ); - } else if (isCheckBoxInput(_f.ref) && _f.refs) { - _f.refs.length > 1 - ? _f.refs.forEach( - (checkboxRef) => - (checkboxRef.checked = Array.isArray(value) - ? !!(value as []).find( - (data: string) => data === checkboxRef.value, - ) - : value === checkboxRef.value), - ) - : (_f.refs[0].checked = !!value); - } else { - _f.ref.value = value; - } + const field = get(fieldsRef.current, name); - if (shouldRender) { - const values = getFieldsValues(fieldsRef); - set(values, name, rawValue); - controllerSubjectRef.current.next({ - values: { - ...defaultValuesRef.current, - ...values, - } as DefaultValues, - name, - }); - } + if (field) { + const _f = (field as Field)._f; + + if (_f) { + const value = + isWeb && isHTMLElement(_f.ref) && isNullOrUndefined(rawValue) + ? '' + : rawValue; + _f.value = getFieldValueAs(rawValue, _f); + + if (isRadioInput(_f.ref)) { + (_f.refs || []).forEach( + (radioRef: HTMLInputElement) => + (radioRef.checked = radioRef.value === value), + ); + } else if (isFileInput(_f.ref) && !isString(value)) { + _f.ref.files = value as FileList; + } else if (isMultipleSelect(_f.ref)) { + [..._f.ref.options].forEach( + (selectRef) => + (selectRef.selected = (value as string[]).includes( + selectRef.value, + )), + ); + } else if (isCheckBoxInput(_f.ref) && _f.refs) { + _f.refs.length > 1 + ? _f.refs.forEach( + (checkboxRef) => + (checkboxRef.checked = Array.isArray(value) + ? !!(value as []).find( + (data: string) => data === checkboxRef.value, + ) + : value === checkboxRef.value), + ) + : (_f.refs[0].checked = !!value); + } else { + _f.ref.value = value; + } + + if (shouldRender) { + const values = getFieldsValues(fieldsRef); + set(values, name, rawValue); + controllerSubjectRef.current.next({ + values: { + ...defaultValuesRef.current, + ...values, + } as DefaultValues, + name, + }); + } - options.shouldDirty && updateAndGetDirtyState(name, value); - options.shouldValidate && trigger(name as Path); + options.shouldDirty && updateAndGetDirtyState(name, value); + options.shouldValidate && trigger(name as Path); + } else { + field._f = { + ref: { + name, + value: rawValue, + }, + value: rawValue, + }; + } } }, [], @@ -601,7 +613,7 @@ export function useForm< set(fieldArrayDefaultValuesRef.current, name, []); } - (field && !field._f) || isFieldArray + ((field && !field._f) || isFieldArray) && !isNullOrUndefined(value) ? setInternalValues(name, value, isFieldArray ? {} : options) : setFieldValue(name, value, options, true, !field);