Skip to content

Commit

Permalink
馃悶 fix #5276 setValue with null (#5278)
Browse files Browse the repository at this point in the history
* fix #5276 set null bug

* include unit test update
  • Loading branch information
bluebill1049 committed May 21, 2021
1 parent 6ef1345 commit dd821f6
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 52 deletions.
80 changes: 80 additions & 0 deletions src/__tests__/useForm/setValue.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<FormData>({
defaultValues: {
user: {
name: 'John Doe',
},
},
});

result = watch();

register('user');

return (
<div>
<button onClick={() => setValue('user', null)}>
Set user to null
</button>
</div>
);
}

render(<App />);

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<FormData>({
defaultValues: {
user: {
name: 'John Doe',
},
},
});

result = watch();

return (
<div>
<button onClick={() => setValue('user', null)}>
Set user to null
</button>
</div>
);
}

render(<App />);

actComponent(() => {
fireEvent.click(screen.getByRole('button'));
});

expect(result).toEqual({
user: null,
});
});
});
});
3 changes: 2 additions & 1 deletion src/logic/getFieldsValues.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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');

Expand Down
114 changes: 63 additions & 51 deletions src/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,58 +245,70 @@ export function useForm<
shouldRegister?: boolean,
) => {
shouldRegister && register(name as Path<TFieldValues>);
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<TFieldValues>,
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<TFieldValues>,
name,
});
}

options.shouldDirty && updateAndGetDirtyState(name, value);
options.shouldValidate && trigger(name as Path<TFieldValues>);
options.shouldDirty && updateAndGetDirtyState(name, value);
options.shouldValidate && trigger(name as Path<TFieldValues>);
} else {
field._f = {
ref: {
name,
value: rawValue,
},
value: rawValue,
};
}
}
},
[],
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit dd821f6

Please sign in to comment.