Skip to content

Commit

Permalink
clean up code for event listener (react-hook-form#200)
Browse files Browse the repository at this point in the history
* clean up code for event listener

* fix unit test

* update gitignore and include one more automation test
  • Loading branch information
bluebill1049 authored Aug 10, 2019
1 parent 05edcfc commit 41374cc
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 77 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ typings/
.rpt2_cache
.coveralls.yml
package-lock.json
/cypress
/cypress/videos
45 changes: 45 additions & 0 deletions cypress/integration/basicSchemaValidation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
context('basicSchemaValidation form validation', () => {
it('should validate the form', () => {
cy.visit('http://localhost:3000/basicSchemaValidation');
cy.get('button').click();

cy.get('input[name="firstName"] + p').contains('firstName error');
cy.get('input[name="lastName"] + p').contains('lastName error');
cy.get('select[name="selectNumber"] + p').contains('selectNumber error');
cy.get('input[name="minRequiredLength"] + p').contains('minRequiredLength error');
cy.get('input[name="radio"] + p').contains('radio error');

cy.get('input[name="firstName"]').type('bill');
cy.get('input[name="lastName"]').type('luo123456');
cy.get('input[name="lastName"] + p').contains('lastName error');
cy.get('select[name="selectNumber"]').select('1');
cy.get('input[name="pattern"]').type('luo');
cy.get('input[name="min"]').type('1');
cy.get('input[name="max"]').type('21');
cy.get('input[name="minDate"]').type('2019-07-30');
cy.get('input[name="maxDate"]').type('2019-08-02');
cy.get('input[name="lastName"]')
.clear()
.type('luo');
cy.get('input[name="minLength"]').type('b');

cy.get('input[name="pattern"] + p').contains('pattern error');
cy.get('input[name="minLength"] + p').contains('minLength error');
cy.get('input[name="min"] + p').contains('min error');
cy.get('input[name="max"] + p').contains('max error');
cy.get('input[name="minDate"] + p').contains('minDate error');
cy.get('input[name="maxDate"] + p').contains('maxDate error');

cy.get('input[name="pattern"]').type('23');
cy.get('input[name="minLength"]').type('bi');
cy.get('input[name="minRequiredLength"]').type('bi');
cy.get('input[name="radio"]').check('1');
cy.get('input[name="min"]').clear().type('11');
cy.get('input[name="max"]').clear().type('19');
cy.get('input[name="minDate"]').type('2019-08-01');
cy.get('input[name="maxDate"]').type('2019-08-01');
cy.get('input[name="checkbox"]').check();

cy.get('p').should('have.length', 0);
});
});
31 changes: 3 additions & 28 deletions src/logic/findRemovedFieldAndRemoveListener.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ describe('findMissDomAndClean', () => {
findRemovedFieldAndRemoveListener(
// @ts-ignore
fields,
new Set(),
new Set(),
() => {},
{
ref: { name: 'bill', type: 'radio' },
Expand All @@ -26,23 +24,6 @@ describe('findMissDomAndClean', () => {
).toEqual(undefined);
});

it('should remove touched Fields reference', () => {
const touchedRefs = new Set(['test', 'test1', 'test3']);
// @ts-ignore
findRemovedFieldAndRemoveListener({}, touchedRefs, new Set(), () => {}, {
ref: { name: 'test', type: 'radio' },
options: [
{
mutationWatcher: {
disconnect: () => {},
},
ref: {},
},
],
});
expect(touchedRefs).toEqual(new Set(['test1', 'test3']));
});

it('should remove options completely if option found and no option left', () => {
document.body.contains = jest.fn(() => false);

Expand All @@ -65,7 +46,7 @@ describe('findMissDomAndClean', () => {
],
},
};
findRemovedFieldAndRemoveListener(fields, new Set(), new Set(), () => {}, {
findRemovedFieldAndRemoveListener(fields, () => {}, {
ref: { name: 'test', type: 'radio' },
options: [
{
Expand Down Expand Up @@ -100,7 +81,7 @@ describe('findMissDomAndClean', () => {
},
};

findRemovedFieldAndRemoveListener(fields, new Set(), new Set(), () => {}, {
findRemovedFieldAndRemoveListener(fields, () => {}, {
ref,
mutationWatcher: {
disconnect,
Expand All @@ -118,8 +99,6 @@ describe('findMissDomAndClean', () => {
findRemovedFieldAndRemoveListener(
// @ts-ignore
fields,
{ current: new Set() },
{ current: new Set() },
() => {},
{},
),
Expand Down Expand Up @@ -151,8 +130,6 @@ describe('findMissDomAndClean', () => {
expect(
findRemovedFieldAndRemoveListener(
fields,
new Set(),
new Set(),
() => {},
{
ref: { name: 'test', type: 'radio' },
Expand Down Expand Up @@ -184,7 +161,7 @@ describe('findMissDomAndClean', () => {
},
};

findRemovedFieldAndRemoveListener(fields, new Set(), new Set(), () => {}, {
findRemovedFieldAndRemoveListener(fields, () => {}, {
ref: { name: 'test', type: 'text' },
options: [
{
Expand All @@ -202,8 +179,6 @@ describe('findMissDomAndClean', () => {
// @ts-ignore
findRemovedFieldAndRemoveListener(
fields,
new Set(),
new Set(),
() => {},
{
ref: { name: 'test', type: 'text' },
Expand Down
11 changes: 3 additions & 8 deletions src/logic/findRemovedFieldAndRemoveListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,16 @@ export default function findRemovedFieldAndRemoveListener<
Data extends DataType
>(
fields: FieldsObject<Data>,
touchedFieldsRef: Set<unknown>,
fieldsWithValidationRef: Set<unknown>,
validateWithStateUpdate: Function,
validateWithStateUpdate: Function | undefined = () => {},
{ ref, mutationWatcher, options }: Field,
forceDelete: boolean = false,
): void {
if (!ref || !ref.type) return;

const { name, type } = ref;
touchedFieldsRef.delete(name);
fieldsWithValidationRef.delete(name);

if (isRadioInput(type) && options) {
options.forEach(({ ref }, index): void => {
if (options[index] && isDetached(ref)) {
if (options[index] && isDetached(ref) || forceDelete) {
removeAllEventListeners(options[index], validateWithStateUpdate);
(
options[index].mutationWatcher || { disconnect: (): void => {} }
Expand All @@ -31,7 +26,7 @@ export default function findRemovedFieldAndRemoveListener<
});

if (!options.length) delete fields[name];
} else if (forceDelete || isDetached(ref)) {
} else if (isDetached(ref) || forceDelete) {
removeAllEventListeners(ref, validateWithStateUpdate);
if (mutationWatcher) mutationWatcher.disconnect();
delete fields[name];
Expand Down
68 changes: 28 additions & 40 deletions src/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,31 +287,27 @@ export default function useForm<
if (shouldUpdateState) reRenderForm({});
};

const removeEventListener: Function = useCallback(
findRemovedFieldAndRemoveListener.bind(
null,
fieldsRef.current,
touchedFieldsRef.current,
fieldsWithValidationRef.current,
validateAndStateUpdateRef.current,
),
[],
);
const resetFieldRef = (name: Name) => {
delete watchFieldsRef.current[name];
delete errorsRef.current[name];
delete fieldsRef.current[name];
touchedFieldsRef.current.delete(name);
fieldsWithValidationRef.current.delete(name);
validFieldsRef.current.delete(name);
};

const removeInputEventListener: Function = useCallback(
field => {
if (!field) return;
const {
ref: { type },
options,
} = field;
isRadioInput(type) && Array.isArray(options)
? options.forEach((fieldRef): void =>
removeEventListener(fieldRef, true),
)
: removeEventListener(field, true);
const removeEventListenerAndRef: Function = useCallback(
(field: Field, forceDelete?: boolean) => {
findRemovedFieldAndRemoveListener(
fieldsRef.current,
validateAndStateUpdateRef.current,
field,
forceDelete,
);

if (field.ref) resetFieldRef(field.ref.name);
},
[removeEventListener],
[],
);

const clearError = (name?: Name | Name[]): void => {
Expand Down Expand Up @@ -396,15 +392,15 @@ export default function useForm<
...inputData,
mutationWatcher: onDomRemove(
elementRef,
(): Function => removeEventListener(inputData, true),
(): Function => removeEventListenerAndRef(inputData),
),
});
} else {
fields[name] = {
...inputData,
mutationWatcher: onDomRemove(
elementRef,
(): Function => removeEventListener(inputData, true),
(): Function => removeEventListenerAndRef(inputData),
),
};
}
Expand Down Expand Up @@ -435,7 +431,7 @@ export default function useForm<
});
}
},
[defaultValues, isOnSubmit, nativeValidation, removeEventListener],
[defaultValues, isOnSubmit, nativeValidation, removeEventListenerAndRef],
);

function watch(
Expand Down Expand Up @@ -501,19 +497,10 @@ export default function useForm<
[registerIntoFieldsRef],
);

const resetField = (name: Name | string) => {
const field = fieldsRef.current[name as string];
removeInputEventListener(field);
delete watchFieldsRef.current[name];
delete errorsRef.current[name];
delete fieldsRef.current[name];
touchedFieldsRef.current.delete(name);
fieldsWithValidationRef.current.delete(name);
validFieldsRef.current.delete(name);
};

const unregister = (name: Name | string | (Name | string)[]): void => {
Array.isArray(name) ? name.forEach(resetField) : resetField(name);
(Array.isArray(name) ? name : [name]).forEach(fieldName =>
removeEventListenerAndRef(fieldsRef.current[fieldName], true),
);
};

const handleSubmit = (callback: OnSubmit<Data>) => async (
Expand Down Expand Up @@ -617,11 +604,12 @@ export default function useForm<
const unSubscribe = useCallback((): void => {
fieldsRef.current &&
Object.values(fieldsRef.current).forEach(
(field: Field | undefined): void => removeInputEventListener(field),
(field: Field | undefined): void =>
removeEventListenerAndRef(field, true),
);
fieldsRef.current = {};
resetRefs();
}, [removeInputEventListener]);
}, [removeEventListenerAndRef]);

const reset = useCallback((values?: DataType): void => {
const fieldValues = Object.values(fieldsRef.current);
Expand Down

0 comments on commit 41374cc

Please sign in to comment.