Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/demo/useWatch-selector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## useWatch-selector

<code src="../examples/useWatch-selector.tsx"></code>
37 changes: 37 additions & 0 deletions docs/examples/useWatch-selector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import Form, { Field } from 'rc-field-form';
import Input from './components/Input';

type FieldType = {
init?: string;
name?: string;
age?: number;
};

export default () => {
const [form] = Form.useForm<FieldType>();
const values = Form.useWatch(
values => ({ init: values.init, newName: values.name, newAge: values.age }),
{ form, preserve: true },
);
console.log('values', values);
return (
<>
<Form form={form} initialValues={{ init: 'init', name: 'aaa' }}>
name
<Field name="name">
<Input />
</Field>
age
<Field name="age">
<Input />
</Field>
no-watch
<Field name="no-watch">
<Input />
</Field>
values:{JSON.stringify(values)}
</Form>
</>
);
};
25 changes: 18 additions & 7 deletions src/useWatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ function useWatch<TForm extends FormInstance>(
form?: TForm | WatchOptions<TForm>,
): GetGeneric<TForm>;

function useWatch<TForm extends FormInstance, TSelected = unknown>(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

因为用了 TSelected,所以不建议 const values = useWatch<{name?: string}>(values => xxx) 设置 stroe 类型方式

Copy link
Contributor Author

Choose a reason for hiding this comment

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

不过还是支持了

selector: (values: GetGeneric<TForm>) => TSelected,
form?: TForm | WatchOptions<TForm>,
): TSelected;

function useWatch<TForm extends FormInstance>(
dependencies: NamePath,
form?: TForm | WatchOptions<TForm>,
Expand All @@ -86,8 +91,10 @@ function useWatch<ValueType = Store>(
form?: FormInstance | WatchOptions<FormInstance>,
): ValueType;

function useWatch(...args: [NamePath, FormInstance | WatchOptions<FormInstance>]) {
const [dependencies = [], _form = {}] = args;
function useWatch(
...args: [NamePath | ((values: Store) => any), FormInstance | WatchOptions<FormInstance>]
) {
const [dependencies, _form = {}] = args;
const options = isFormInstance(_form) ? { form: _form } : _form;
const form = options.form;

Expand Down Expand Up @@ -125,8 +132,15 @@ function useWatch(...args: [NamePath, FormInstance | WatchOptions<FormInstance>]
const { getFieldsValue, getInternalHooks } = formInstance;
const { registerWatch } = getInternalHooks(HOOK_MARK);

const getNewValue = (values: any, allValues: any) => {
Copy link
Member

Choose a reason for hiding this comment

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

-> getWatchValue

const _values = options.preserve ? allValues : values;
Copy link
Member

Choose a reason for hiding this comment

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

不要用下划线命名,rawValues

Copy link
Contributor Author

Choose a reason for hiding this comment

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

提出来了

return typeof dependencies === 'function'
? dependencies(_values)
: getValue(_values, namePathRef.current);
};

const cancelRegister = registerWatch((values, allValues) => {
const newValue = getValue(options.preserve ? allValues : values, namePathRef.current);
const newValue = getNewValue(values, allValues);
const nextValueStr = stringify(newValue);

// Compare stringify in case it's nest object
Expand All @@ -137,10 +151,7 @@ function useWatch(...args: [NamePath, FormInstance | WatchOptions<FormInstance>]
});

// TODO: We can improve this perf in future
const initialValue = getValue(
options.preserve ? getFieldsValue(true) : getFieldsValue(),
namePathRef.current,
);
const initialValue = getNewValue(getFieldsValue(), getFieldsValue(true));

// React 18 has the bug that will queue update twice even the value is not changed
// ref: https://github.com/facebook/react/issues/27213
Expand Down
25 changes: 25 additions & 0 deletions tests/useWatch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -457,4 +457,29 @@ describe('useWatch', () => {

logSpy.mockRestore();
});
it('selector', async () => {
const Demo: React.FC = () => {
const [form] = Form.useForm<{ name?: string }>();
const nameValue = Form.useWatch(values => values.name, form);
return (
<div>
<Form form={form}>
<Field name="name" initialValue="bamboo">
<Input />
</Field>
</Form>
<div className="values">{nameValue}</div>
</div>
);
};

const { container } = render(<Demo />);
await act(async () => {
await timeout();
});
expect(container.querySelector<HTMLDivElement>('.values')?.textContent).toEqual('bamboo');
const input = container.querySelectorAll<HTMLInputElement>('input');
await changeValue(input[0], 'bamboo2');
expect(container.querySelector<HTMLDivElement>('.values')?.textContent).toEqual('bamboo2');
});
});