Skip to content

Commit

Permalink
seperate proxy subscription between hook and subscribe function
Browse files Browse the repository at this point in the history
  • Loading branch information
bluebill1049 committed Feb 24, 2024
1 parent 43cdae4 commit bb914fe
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 18 deletions.
69 changes: 67 additions & 2 deletions src/__tests__/useForm/subscribe.test.tsx
@@ -1,14 +1,19 @@
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';

import { createFormControl } from '../../logic';
import { useForm } from '../../useForm';

describe('subscribe', () => {
it('should subscribe to correct form state', () => {
it('should subscribe to correct form state and prevent form re-render', async () => {
const callback = jest.fn();

const { subscribe, register, setValue } = createFormControl({
const control = createFormControl({
defaultValues: {
test: '',
},
});
const { subscribe, register, setValue } = control;

subscribe({
formState: {
Expand Down Expand Up @@ -39,6 +44,66 @@ describe('subscribe', () => {
disabled: false,
name: 'test',
});

let renderCount = 0;

const App = () => {
useForm({
control,
});

renderCount++;

return <p>{renderCount === 1 ? 'yes' : 'no'}</p>;
};

render(<App />);

await waitFor(() => {
screen.getByText('yes');
});
});

it('should re-render by hook form state subscription', async () => {
const callback = jest.fn();

const control = createFormControl({
defaultValues: {
test: '',
},
});
const { subscribe, register, setValue } = control;

subscribe({
formState: {
values: true,
},
callback,
});

register('test');
setValue('test', 'data', { shouldDirty: true });

let renderCount = 0;

const App = () => {
const {
formState: { isDirty },
} = useForm({
control,
});

isDirty;
renderCount++;

return <p>{renderCount === 2 ? 'yes' : 'no'}</p>;
};

render(<App />);

await waitFor(() => {
screen.getByText('yes');
});
});

it('should only subscribe to individual field', () => {
Expand Down
60 changes: 44 additions & 16 deletions src/logic/createFormControl.ts
Expand Up @@ -142,7 +142,7 @@ export function createFormControl<
};
let delayErrorCallback: DelayCallback | null;
let timer = 0;
let _proxyFormState: ReadFormState = {
const _proxyFormState: ReadFormState = {
isDirty: false,
dirtyFields: false,
validatingFields: false,
Expand All @@ -151,6 +151,9 @@ export function createFormControl<
isValid: false,
errors: false,
};
let _proxySubscribeFormState = {
..._proxyFormState,
};
const _subjects: Subjects<TFieldValues> = {
array: createSubject(),
state: createSubject(),
Expand All @@ -168,7 +171,11 @@ export function createFormControl<
};

const _updateValid = async (shouldUpdateValid?: boolean) => {
if (_proxyFormState.isValid || shouldUpdateValid) {
if (
_proxyFormState.isValid ||
_proxySubscribeFormState.isValid ||
shouldUpdateValid
) {
const isValid = _options.resolver
? isEmptyObject((await _executeSchema()).errors)
: await executeBuiltInValidation(_fields, true);
Expand All @@ -182,7 +189,14 @@ export function createFormControl<
};

const _updateIsValidating = (isValidating: boolean, names: string[]) => {
if (!(_proxyFormState.isValidating || _proxyFormState.validatingFields)) {
if (
!(
_proxyFormState.isValidating ||
_proxyFormState.validatingFields ||
_proxySubscribeFormState.isValidating ||
_proxySubscribeFormState.validatingFields
)
) {
return;
}
names.forEach((name) => {
Expand Down Expand Up @@ -224,7 +238,8 @@ export function createFormControl<
}

if (
_proxyFormState.touchedFields &&
(_proxyFormState.touchedFields ||
_proxySubscribeFormState.touchedFields) &&
shouldUpdateFieldsAndState &&
Array.isArray(get(_formState.touchedFields, name))
) {
Expand All @@ -236,7 +251,7 @@ export function createFormControl<
shouldSetValues && set(_formState.touchedFields, name, touchedFields);
}

if (_proxyFormState.dirtyFields) {
if (_proxyFormState.dirtyFields || _proxySubscribeFormState.dirtyFields) {
_formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
}

Expand Down Expand Up @@ -315,7 +330,7 @@ export function createFormControl<
);

if (!isBlurEvent || shouldDirty) {
if (_proxyFormState.isDirty) {
if (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty) {
isPreviousDirty = _formState.isDirty;
_formState.isDirty = output.isDirty = _getDirty();
shouldUpdateField = isPreviousDirty !== output.isDirty;
Expand All @@ -331,7 +346,8 @@ export function createFormControl<
output.dirtyFields = _formState.dirtyFields;
shouldUpdateField =
shouldUpdateField ||
(_proxyFormState.dirtyFields &&
((_proxyFormState.dirtyFields ||
_proxySubscribeFormState.dirtyFields) &&
isPreviousDirty !== !isCurrentFieldPristine);
}

Expand All @@ -343,7 +359,8 @@ export function createFormControl<
output.touchedFields = _formState.touchedFields;
shouldUpdateField =
shouldUpdateField ||
(_proxyFormState.touchedFields &&
((_proxyFormState.touchedFields ||
_proxySubscribeFormState.touchedFields) &&
isPreviousFieldTouched !== isBlurEvent);
}
}
Expand All @@ -365,7 +382,7 @@ export function createFormControl<
) => {
const previousFieldError = get(_formState.errors, name);
const shouldUpdateValid =
_proxyFormState.isValid &&
(_proxyFormState.isValid || _proxySubscribeFormState.isValid) &&
isBoolean(isValid) &&
_formState.isValid !== isValid;

Expand Down Expand Up @@ -657,7 +674,10 @@ export function createFormControl<
});

if (
(_proxyFormState.isDirty || _proxyFormState.dirtyFields) &&
(_proxyFormState.isDirty ||
_proxyFormState.dirtyFields ||
_proxySubscribeFormState.isDirty ||
_proxySubscribeFormState.dirtyFields) &&
options.shouldDirty
) {
_subjects.state.next({
Expand Down Expand Up @@ -738,7 +758,8 @@ export function createFormControl<
});

if (shouldSkipValidation) {
_proxyFormState.isValid && _updateValid();
(_proxyFormState.isValid || _proxySubscribeFormState.isValid) &&
_updateValid();

return (
shouldRender &&
Expand Down Expand Up @@ -787,7 +808,10 @@ export function createFormControl<
if (isFieldValueUpdated) {
if (error) {
isValid = false;
} else if (_proxyFormState.isValid) {
} else if (
_proxyFormState.isValid ||
_proxySubscribeFormState.isValid
) {
isValid = await executeBuiltInValidation(_fields, true);
}
}
Expand Down Expand Up @@ -847,7 +871,8 @@ export function createFormControl<

_subjects.state.next({
...(!isString(name) ||
(_proxyFormState.isValid && isValid !== _formState.isValid)
((_proxyFormState.isValid || _proxySubscribeFormState.isValid) &&
isValid !== _formState.isValid)
? {}
: { name }),
...(_options.resolver || !name ? { isValid } : {}),
Expand Down Expand Up @@ -975,11 +1000,14 @@ export function createFormControl<

const subscribe: UseFromSubscribe<TFieldValues> = (props) => {
_state.mount = true;
_proxyFormState = {
..._proxyFormState,
_proxySubscribeFormState = {
..._proxySubscribeFormState,
...props.formState,
};
return _subscribe(props);
return _subscribe({
...props,
formState: _proxySubscribeFormState,
});
};

const unregister: UseFormUnregister<TFieldValues> = (name, options = {}) => {
Expand Down

0 comments on commit bb914fe

Please sign in to comment.