Skip to content
Merged
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
3a1c5e9
feat: init
crazyair Apr 6, 2022
73feed0
feat: watch
crazyair Apr 6, 2022
9833b8e
feat: watch
crazyair Apr 7, 2022
e8b6e22
feat: watch
crazyair Apr 7, 2022
595029f
feat: watch
crazyair Apr 7, 2022
06e74ef
feat: watch
crazyair Apr 7, 2022
2470933
feat: watch
crazyair Apr 7, 2022
25c182a
feat: watch
crazyair Apr 7, 2022
ab0c98a
fix: resetFields
crazyair Apr 7, 2022
806017b
feat: watch
crazyair Apr 7, 2022
cdd5b00
feat: watch
crazyair Apr 7, 2022
35bb4e1
feat: watch
crazyair Apr 7, 2022
01ada2b
feat: watch
crazyair Apr 7, 2022
deaf6b8
feat: add test
crazyair Apr 7, 2022
df922c6
feat: add test
crazyair Apr 7, 2022
fc0aa8a
feat: add test
crazyair Apr 7, 2022
d425c47
feat: add test
crazyair Apr 7, 2022
d6bd249
feat: add test
crazyair Apr 7, 2022
64af139
feat: add test
crazyair Apr 7, 2022
72d6b7f
feat: add test
crazyair Apr 7, 2022
d387c82
feat: test
crazyair Apr 8, 2022
63fe68b
feat: test
crazyair Apr 8, 2022
ab9541b
feat: test
crazyair Apr 8, 2022
f091a8d
feat: add list
crazyair Apr 8, 2022
8601a85
feat: add list
crazyair Apr 8, 2022
77b6e71
feat: add test
crazyair Apr 8, 2022
73f42e1
feat: add demo
crazyair Apr 8, 2022
d585905
feat: add demo
crazyair Apr 8, 2022
4be1562
feat: api
crazyair Apr 8, 2022
eda6659
feat: api
crazyair Apr 8, 2022
19218df
feat: review
crazyair Apr 8, 2022
ee50f0c
feat: watchId
crazyair Apr 8, 2022
966980e
feat: watch
crazyair Apr 8, 2022
bc11e1e
feat: review
crazyair Apr 8, 2022
a1f62bc
feat: review
crazyair Apr 8, 2022
1d1e31d
feat: all values
crazyair Apr 9, 2022
5334673
feat: all values
crazyair Apr 9, 2022
ccdc2d0
feat: all values
crazyair Apr 9, 2022
c73a943
feat: all values
crazyair Apr 9, 2022
bb1c045
feat: test
crazyair Apr 9, 2022
d338c52
feat: watch
crazyair Apr 9, 2022
e74b6c1
feat: watch
crazyair Apr 9, 2022
0c41cc1
feat: watch
crazyair Apr 9, 2022
c9fb129
feat: watch
crazyair Apr 9, 2022
8b0acda
feat: watch
crazyair Apr 9, 2022
04885af
feat: watch
crazyair Apr 9, 2022
8b0373e
feat: file name
crazyair Apr 9, 2022
ac43148
feat: watch
crazyair Apr 9, 2022
71f2331
feat: watch
crazyair Apr 9, 2022
3639a17
feat: watch
crazyair Apr 9, 2022
b5ad67a
feat: watch
crazyair Apr 9, 2022
40a83ea
feat: watch
crazyair Apr 10, 2022
048086c
feat: watch
crazyair Apr 10, 2022
02b4a71
feat: watch
crazyair Apr 10, 2022
2d947bd
feat: watch
crazyair Apr 10, 2022
ce7f7cb
feat: watch
crazyair Apr 10, 2022
cd20496
feat: watch
crazyair Apr 10, 2022
38e094e
feat: review
crazyair Apr 11, 2022
2bf2011
feat: review
crazyair Apr 11, 2022
f59a635
feat: review
crazyair Apr 11, 2022
8e85500
feat: review
crazyair Apr 11, 2022
309fcdd
feat: review
crazyair Apr 11, 2022
04c631d
feat: review
crazyair Apr 11, 2022
ce1714f
feat: review
crazyair Apr 11, 2022
aabbca1
feat: add demo
crazyair Apr 11, 2022
3dc64f2
feat: values
crazyair Apr 11, 2022
d6c0a2a
feat: remove getRegisterFieldsValue
crazyair Apr 11, 2022
785e07d
feat: watch
crazyair Apr 11, 2022
8d2aa07
feat: remove setWatchCallbacks
crazyair Apr 11, 2022
6272d97
feat: submit demo
crazyair Apr 11, 2022
455b38a
feat: remove ref id
crazyair Apr 11, 2022
807e6e4
feat: ts
crazyair Apr 12, 2022
e79f670
feat: rename
crazyair Apr 12, 2022
1ba2e80
feat: init map
crazyair Apr 12, 2022
0f0bb0e
refactor: Internal namePath logic adjust
zombieJ Apr 13, 2022
d254e8a
chore: misc update
zombieJ Apr 13, 2022
420f0e4
fix: not crash if form is not exist
zombieJ Apr 13, 2022
9c876c5
fix: form warning check
zombieJ Apr 13, 2022
9a78c38
chore: adjust init logic
zombieJ Apr 13, 2022
f6b27b6
chore: clean up
zombieJ Apr 13, 2022
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-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## useWatch-list

<code src="../examples/useWatch-list.tsx" />
3 changes: 3 additions & 0 deletions docs/demo/useWatch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## useWatch

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

const { List, useForm } = Form;

const Demo = () => {
const [form] = useForm();
const list = Form.useWatch(['users'], form);
const values = Form.useWatch(
list?.users?.map((_, index) => ['users', index]),
form,
);

console.log('values', values);

return (
<div>
<Form form={form} style={{ border: '1px solid red', padding: 15 }}>
list length:{list?.users?.length}
<br />
values: {JSON.stringify(values, null, 2)}
<Field name="main">
<Input />
</Field>
<List name="users" initialValue={['bamboo', 'light']}>
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<Field key={field.key} {...field} rules={[{ required: true }]}>
{control => (
<div style={{ display: 'flex', alignItems: 'center' }}>
{index + 1}
<Input {...control} />
<a onClick={() => remove(index)}>Remove</a>
</div>
)}
</Field>
))}
<button type="button" onClick={() => add()}>
+ New User
</button>
</div>
);
}}
</List>
</Form>
</div>
);
};

export default Demo;
101 changes: 101 additions & 0 deletions docs/examples/useWatch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, { useState } from 'react';
import Form, { Field } from 'rc-field-form';
import Input from './components/Input';

let x = 0;

const Demo = React.memo(() => {
const values = Form.useWatch(['demo']);
console.log('demo watch', values);
return (
<Field name="demo">
<Input />
</Field>
);
});
const Demo2 = React.memo(() => {
const values = Form.useWatch(['demo2']);
console.log('demo2 watch', values);
return (
<Field name="demo2">
<Input />
</Field>
);
});

export default () => {
const [form] = Form.useForm();
const [visible, setVisible] = useState(true);
const [visible2, setVisible2] = useState(true);
const [visible3, setVisible3] = useState(true);
const values = Form.useWatch(['name', 'age', 'initialValue'], form);
console.log('main watch', values);
return (
<>
<Form
form={form}
initialValues={{ id: 1, age: '10', name: 'default' }}
onFinish={v => console.log('submit values', v)}
>
no render
<Field name="main">
<Input />
</Field>
name
{visible && (
<Field name="name">
<Input />
</Field>
)}
age
<Field name="age">
<Input />
</Field>
initialValue
{visible3 && (
<Field name="initialValue" initialValue="initialValue">
<Input />
</Field>
)}
name、age 改变 render
<Field dependencies={['field_1']}>
{() => {
x += 1;
return ` ${x}`;
}}
</Field>
<br />
demo1
<Demo />
demo2
{visible2 && <Demo2 />}
<button type="submit">submit</button>
</Form>
<button
onClick={() => {
console.log('values', form.getFieldsValue());
console.log('values all', form.getFieldsValue(true));
}}
>
getFieldsValue
</button>
<button
onClick={() => {
form.setFields([
{ name: 'name', value: 'name' },
{ name: 'age', value: 'age' },
]);
}}
>
setFields
</button>
<button onClick={() => form.resetFields()}>resetFields</button>
<button onClick={() => form.setFieldsValue({ name: `${form.getFieldValue('name') || ''}1` })}>
setFieldsValue
</button>
<button onClick={() => setVisible(c => !c)}>isShow name</button>
<button onClick={() => setVisible3(c => !c)}>isShow initialValue</button>
<button onClick={() => setVisible2(c => !c)}>isShow demo2</button>
</>
);
};
1 change: 1 addition & 0 deletions src/FieldContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const Context = React.createContext<InternalFormInstance>({
setInitialValues: warningFunc,
destroyForm: warningFunc,
setCallbacks: warningFunc,
registerWatch: warningFunc,
getFields: warningFunc,
setValidateMessages: warningFunc,
setPreserve: warningFunc,
Expand Down
3 changes: 3 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import FieldForm, { FormProps } from './Form';
import { FormProvider } from './FormContext';
import FieldContext from './FieldContext';
import ListContext from './ListContext';
import useWatch from './useWatch';

const InternalForm = React.forwardRef<FormInstance, FormProps>(FieldForm) as <Values = any>(
props: FormProps<Values> & { ref?: React.Ref<FormInstance<Values>> },
Expand All @@ -18,6 +19,7 @@ interface RefFormType extends InternalFormType {
Field: typeof Field;
List: typeof List;
useForm: typeof useForm;
useWatch: typeof useWatch;
}

const RefForm: RefFormType = InternalForm as RefFormType;
Expand All @@ -26,6 +28,7 @@ RefForm.FormProvider = FormProvider;
RefForm.Field = Field;
RefForm.List = List;
RefForm.useForm = useForm;
RefForm.useWatch = useWatch;

export { FormInstance, Field, List, useForm, FormProvider, FormProps, FieldContext, ListContext };

Expand Down
3 changes: 3 additions & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ export interface Callbacks<Values = any> {
onFinishFailed?: (errorInfo: ValidateErrorEntity<Values>) => void;
}

export type WatchCallBack = (values: Store, namePathList: InternalNamePath[]) => void;

export interface InternalHooks {
dispatch: (action: ReducerAction) => void;
initEntityValue: (entity: FieldEntity) => void;
Expand All @@ -201,6 +203,7 @@ export interface InternalHooks {
setInitialValues: (values: Store, init: boolean) => void;
destroyForm: () => void;
setCallbacks: (callbacks: Callbacks) => void;
registerWatch: (callback: WatchCallBack) => () => void;
getFields: (namePathList?: InternalNamePath[]) => FieldData[];
setValidateMessages: (validateMessages: ValidateMessages) => void;
setPreserve: (preserve?: boolean) => void;
Expand Down
43 changes: 40 additions & 3 deletions src/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
InternalFieldData,
ValuedNotifyInfo,
RuleError,
WatchCallBack,
} from './interface';
import { HOOK_MARK } from './FieldContext';
import { allPromiseFinish } from './utils/asyncUtil';
Expand Down Expand Up @@ -114,6 +115,7 @@ export class FormStore {
getFields: this.getFields,
setPreserve: this.setPreserve,
getInitialValue: this.getInitialValue,
registerWatch: this.registerWatch,
};
}

Expand Down Expand Up @@ -141,6 +143,7 @@ export class FormStore {

// We will take consider prev form unmount fields.
// When the field is not `preserve`, we need fill this with initialValues instead of store.
// eslint-disable-next-line array-callback-return
this.prevWithoutPreserves?.map(({ key: namePath }) => {
nextStore = setValue(nextStore, namePath, getValue(initialValues, namePath));
});
Expand Down Expand Up @@ -180,6 +183,28 @@ export class FormStore {
this.preserve = preserve;
};

// ============================= Watch ============================
private watchList: WatchCallBack[] = [];

private registerWatch: InternalHooks['registerWatch'] = callback => {
this.watchList.push(callback);

return () => {
this.watchList = this.watchList.filter(fn => fn !== callback);
};
};

private notifyWatch = (namePath: InternalNamePath[] = []) => {
// No need to cost perf when nothing need to watch
if (this.watchList.length) {
const values = this.getFieldsValue();

this.watchList.forEach(callback => {
callback(values, namePath);
});
}
};

// ========================== Dev Warning =========================
private timeoutId: any = null;

Expand Down Expand Up @@ -498,6 +523,7 @@ export class FormStore {
this.updateStore(setValues({}, this.initialValues));
this.resetWithFieldInitialValue();
this.notifyObservers(prevStore, null, { type: 'reset' });
this.notifyWatch();
return;
}

Expand All @@ -509,16 +535,20 @@ export class FormStore {
});
this.resetWithFieldInitialValue({ namePathList });
this.notifyObservers(prevStore, namePathList, { type: 'reset' });
this.notifyWatch(namePathList);
};

private setFields = (fields: FieldData[]) => {
this.warningUnhooked();

const prevStore = this.store;

const namePathList: InternalNamePath[] = [];

fields.forEach((fieldData: FieldData) => {
const { name, errors, ...data } = fieldData;
const namePath = getNamePath(name);
namePathList.push(namePath);

// Value
if ('value' in data) {
Expand All @@ -530,6 +560,8 @@ export class FormStore {
data: fieldData,
});
});

this.notifyWatch(namePathList);
};

private getFields = (): InternalFieldData[] => {
Expand Down Expand Up @@ -573,6 +605,8 @@ export class FormStore {

private registerField = (entity: FieldEntity) => {
this.fieldEntities.push(entity);
const namePath = entity.getNamePath();
this.notifyWatch([namePath]);

// Set initial values
if (entity.props.initialValue !== undefined) {
Expand All @@ -591,8 +625,6 @@ export class FormStore {
const mergedPreserve = preserve !== undefined ? preserve : this.preserve;

if (mergedPreserve === false && (!isListField || subNamePath.length > 1)) {
const namePath = entity.getNamePath();

const defaultValue = isListField ? undefined : this.getInitialValue(namePath);

if (
Expand All @@ -614,6 +646,8 @@ export class FormStore {
this.triggerDependenciesUpdate(prevStore, namePath);
}
}

this.notifyWatch([namePath]);
Copy link
Member

Choose a reason for hiding this comment

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

这里的 notifyWatch 感觉位置不太对。。是不是该跟着 updateStore 后面?
或者有没有可能可以把 notifyWatch notifyObservers updateStore 合成一个方法?逻辑上应该是串联的

Copy link
Contributor

@crazyair crazyair Apr 13, 2022

Choose a reason for hiding this comment

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

本来可以把 notifyWatch 放到 notifyObservers 里,但是还有一点点不同,所以区分开了。不然也容易混乱

Copy link
Member Author

Choose a reason for hiding this comment

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

@MadCcc OK 不?~

Copy link
Member

Choose a reason for hiding this comment

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

感觉上问题不大,setState 相同 value 也不会触发 render

Copy link
Contributor

Choose a reason for hiding this comment

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

会有一次缓存问题(会多 render 一次),明天再看看

};
};

Expand Down Expand Up @@ -679,6 +713,7 @@ export class FormStore {
type: 'valueUpdate',
source: 'internal',
});
this.notifyWatch([namePath]);

// Dependencies update
const childrenFields = this.triggerDependenciesUpdate(prevStore, namePath);
Expand All @@ -701,13 +736,15 @@ export class FormStore {
const prevStore = this.store;

if (store) {
this.updateStore(setValues(this.store, store));
const nextStore = setValues(this.store, store);
this.updateStore(nextStore);
}

this.notifyObservers(prevStore, null, {
type: 'valueUpdate',
source: 'external',
});
this.notifyWatch();
};

private getDependencyChildrenFields = (rootNamePath: InternalNamePath): InternalNamePath[] => {
Expand Down
Loading