Skip to content

Commit ac52393

Browse files
authored
adjust deps logic (#6)
1 parent dfc4a0f commit ac52393

File tree

7 files changed

+56
-10
lines changed

7 files changed

+56
-10
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ We use typescript to create the Type definition. You can view directly in IDE. B
7575

7676
| Prop | Description | Type | Default |
7777
| --- | --- | --- | --- |
78-
| name | Field name path | [NamePath](#namepath)[] | - |
78+
| dependencies | Will re-render if dependencies changed | [NamePath](#namepath)[] | - |
79+
| name | Field name path | [NamePath](#namepath) | - |
7980
| rules | Validate rules | [Rule](#rule)[] | - |
8081
| shouldUpdate | Check if Field should update | (prevValues, nextValues): boolean | - |
8182
| trigger | Collect value update by event trigger | string | onChange |

examples/StateForm-basic.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export default class Demo extends React.Component {
3535
</Field>
3636

3737
<h4>Show additional field when `username` is `111`</h4>
38-
<Field shouldUpdate={(prev, next) => prev.username !== next.username}>
38+
<Field dependencies={['username']}>
3939
{(control, meta, context) => {
4040
const { username } = context.getFieldsValue();
4141
return username === '111' ? <Input {...control} placeholder="I am secret!" /> : null;

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"devDependencies": {
4343
"@types/react": "^16.8.19",
4444
"@types/react-dom": "^16.8.4",
45+
"@types/warning": "^3.0.0",
4546
"enzyme": "^3.1.0",
4647
"enzyme-adapter-react-16": "^1.0.2",
4748
"enzyme-to-json": "^3.1.4",
@@ -50,15 +51,13 @@
5051
"react-dom": "^16.8.6",
5152
"react-redux": "^4.4.10",
5253
"react-router": "^3.0.0",
53-
"react-test-renderer": "^16.1.1",
5454
"redux": "^3.7.2"
5555
},
5656
"pre-commit": [
5757
"lint"
5858
],
5959
"dependencies": {
6060
"async-validator": "^1.11.2",
61-
"babel-runtime": "6.x",
6261
"lodash": "^4.17.4",
6362
"rc-util": "^4.6.0",
6463
"warning": "^4.0.3"

src/Field.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ export interface FieldProps {
3636
| React.ReactElement
3737
| ((control: ChildProps, meta: Meta, form: FormInstance) => React.ReactNode);
3838
rules?: Rule[];
39-
/** Set up `dependencies` field. When dependencies field update and current field is touched, will trigger validate rules. */
39+
/**
40+
* Set up `dependencies` field.
41+
* When dependencies field update and current field is touched, will trigger validate rules and render.
42+
*/
4043
dependencies?: NamePath[];
4144
trigger?: string;
4245
validateTrigger?: string | string[];
@@ -123,7 +126,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
123126
namePathList: InternalNamePath[] | null,
124127
info: NotifyInfo,
125128
) => {
126-
const { shouldUpdate } = this.props;
129+
const { shouldUpdate, dependencies = [] } = this.props;
127130
const { getFieldsValue, getFieldError }: FormInstance = this.context;
128131
const values = getFieldsValue();
129132
const namePath = this.getNamePath();
@@ -169,11 +172,15 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
169172
default:
170173
/**
171174
* - If `namePath` exists in `namePathList`, means it's related value and should update.
175+
* - If `dependencies` exists in `namePathList`, means upstream trigger update.
172176
* - If `shouldUpdate` provided, use customize logic to update the field
173177
* - else to check if value changed
174178
*/
175179
if (
176180
(namePathList && containsNamePath(namePathList, namePath)) ||
181+
dependencies.some(dependency =>
182+
containsNamePath(namePathList, getNamePath(dependency)),
183+
) ||
177184
(shouldUpdate ? shouldUpdate(prevStore, values, info) : prevValue !== curValue)
178185
) {
179186
this.forceUpdate();
@@ -327,7 +334,8 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
327334

328335
const { child, isFunction } = this.getOnlyChild(children);
329336
if (!child) {
330-
return children;
337+
// Return origin `children` if is not a function
338+
return isFunction ? child : children;
331339
}
332340

333341
// Not need to `cloneElement` since user can handle this in render function self

src/useForm.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ export class FormStore {
324324
onValuesChange(changedValues, this.store);
325325
}
326326

327-
this.triggerOnFieldsChange([namePath]);
327+
this.triggerOnFieldsChange([namePath, ...childrenFields]);
328328
};
329329

330330
// Let all child Field get update.
@@ -365,8 +365,8 @@ export class FormStore {
365365
if (!children.has(field)) {
366366
children.add(field);
367367

368-
if (field.isFieldTouched()) {
369-
const fieldNamePath = field.getNamePath();
368+
const fieldNamePath = field.getNamePath();
369+
if (field.isFieldTouched() && fieldNamePath.length) {
370370
childrenFields.push(fieldNamePath);
371371
fillChildren(fieldNamePath);
372372
}

tests/common/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import timeout from './timeout';
2+
import InfoField from './InfoField';
23

34
export async function changeValue(wrapper, value) {
45
wrapper.find('input').simulate('change', { target: { value } });
@@ -17,3 +18,7 @@ export function matchError(wrapper, error) {
1718
expect(wrapper.find('.errors li').text()).toBe(error);
1819
}
1920
}
21+
22+
export function getField(wrapper, index = 0) {
23+
return wrapper.find(InfoField).at(index);
24+
}

tests/dependencies.test.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react';
2+
import { mount } from 'enzyme';
3+
import Form from '../src';
4+
import InfoField from './common/InfoField';
5+
import { changeValue, matchError, getField } from './common';
6+
7+
describe('dependencies', () => {
8+
it('touched', async () => {
9+
let form = null;
10+
11+
const wrapper = mount(
12+
<div>
13+
<Form
14+
ref={instance => {
15+
form = instance;
16+
}}
17+
>
18+
<InfoField name="field_1" />
19+
<InfoField name="field_2" rules={[{ required: true }]} dependencies={['field_1']} />
20+
</Form>
21+
</div>,
22+
);
23+
24+
// Not trigger if not touched
25+
await changeValue(getField(wrapper, 0), '');
26+
matchError(getField(wrapper, 1), false);
27+
28+
// Trigger if touched
29+
form.setFields([{ name: 'field_2', touched: true }]);
30+
await changeValue(getField(wrapper, 0), '');
31+
matchError(getField(wrapper, 1), true);
32+
});
33+
});

0 commit comments

Comments
 (0)