Skip to content

Commit

Permalink
fix: useWatch object trigger re-render (#433)
Browse files Browse the repository at this point in the history
* test: test driven

* test: test driven

* fix: object path
  • Loading branch information
zombieJ authored May 6, 2022
1 parent a1dd96d commit 7268791
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 4 deletions.
21 changes: 17 additions & 4 deletions src/useWatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ import { FieldContext } from '.';
import warning from 'rc-util/lib/warning';
import { HOOK_MARK } from './FieldContext';
import type { InternalFormInstance, NamePath, Store } from './interface';
import { useState, useContext, useEffect, useRef } from 'react';
import { useState, useContext, useEffect, useRef, useMemo } from 'react';
import { getNamePath, getValue } from './utils/valueUtil';

type ReturnPromise<T> = T extends Promise<infer ValueType> ? ValueType : never;
type GetGeneric<TForm extends FormInstance> = ReturnPromise<ReturnType<TForm['validateFields']>>;

function stringify(value: any) {
try {
return JSON.stringify(value);
} catch (err) {
return Math.random();
}
}

function useWatch<
TDependencies1 extends keyof GetGeneric<TForm>,
TForm extends FormInstance,
Expand Down Expand Up @@ -52,8 +60,10 @@ function useWatch<ValueType = Store>(dependencies: NamePath, form?: FormInstance

function useWatch(dependencies: NamePath = [], form?: FormInstance) {
const [value, setValue] = useState<any>();
const valueCacheRef = useRef<any>();
valueCacheRef.current = value;

const valueStr = useMemo(() => stringify(value), [value]);
const valueStrRef = useRef(valueStr);
valueStrRef.current = valueStr;

const fieldContext = useContext(FieldContext);
const formInstance = (form as InternalFormInstance) || fieldContext;
Expand Down Expand Up @@ -83,7 +93,10 @@ function useWatch(dependencies: NamePath = [], form?: FormInstance) {

const cancelRegister = registerWatch(store => {
const newValue = getValue(store, namePathRef.current);
if (valueCacheRef.current !== newValue) {
const nextValueStr = stringify(newValue);

// Compare stringify in case it's nest object
if (valueStrRef.current !== nextValueStr) {
setValue(newValue);
}
});
Expand Down
43 changes: 43 additions & 0 deletions tests/useWatch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ describe('useWatch', () => {
});
expect(renderTime).toEqual(2);
});

it('typescript', () => {
type FieldType = {
main?: string;
Expand Down Expand Up @@ -307,4 +308,46 @@ describe('useWatch', () => {

mount(<Demo />);
});

// https://github.com/react-component/field-form/issues/431
it('not trigger effect', () => {
let updateA = 0;
let updateB = 0;

const Demo = () => {
const [form] = Form.useForm();
const userA = Form.useWatch(['a'], form);
const userB = Form.useWatch(['b'], form);

React.useEffect(() => {
updateA += 1;
console.log('Update A', userA);
}, [userA]);
React.useEffect(() => {
updateB += 1;
console.log('Update B', userB);
}, [userB]);

return (
<Form form={form}>
<Field name={['a', 'name']}>
<Input />
</Field>
<Field name={['b', 'name']}>
<Input />
</Field>
</Form>
);
};

const wrapper = mount(<Demo />);

console.log('Change!');
wrapper
.find('input')
.first()
.simulate('change', { target: { value: 'bamboo' } });

expect(updateA > updateB).toBeTruthy();
});
});

1 comment on commit 7268791

@vercel
Copy link

@vercel vercel bot commented on 7268791 May 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.