Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ We use typescript to create the Type definition. You can view directly in IDE. B

| Prop | Description | Type | Default |
| --- | --- | --- | --- |
| name | Field name path | [NamePath](#namepath)[] | - |
| dependencies | Will re-render if dependencies changed | [NamePath](#namepath)[] | - |
| name | Field name path | [NamePath](#namepath) | - |
| rules | Validate rules | [Rule](#rule)[] | - |
| shouldUpdate | Check if Field should update | (prevValues, nextValues): boolean | - |
| trigger | Collect value update by event trigger | string | onChange |
Expand Down
2 changes: 1 addition & 1 deletion examples/StateForm-basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default class Demo extends React.Component {
</Field>

<h4>Show additional field when `username` is `111`</h4>
<Field shouldUpdate={(prev, next) => prev.username !== next.username}>
<Field dependencies={['username']}>
{(control, meta, context) => {
const { username } = context.getFieldsValue();
return username === '111' ? <Input {...control} placeholder="I am secret!" /> : null;
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"devDependencies": {
"@types/react": "^16.8.19",
"@types/react-dom": "^16.8.4",
"@types/warning": "^3.0.0",
"enzyme": "^3.1.0",
"enzyme-adapter-react-16": "^1.0.2",
"enzyme-to-json": "^3.1.4",
Expand All @@ -50,15 +51,13 @@
"react-dom": "^16.8.6",
"react-redux": "^4.4.10",
"react-router": "^3.0.0",
"react-test-renderer": "^16.1.1",
"redux": "^3.7.2"
},
"pre-commit": [
"lint"
],
"dependencies": {
"async-validator": "^1.11.2",
"babel-runtime": "6.x",
"lodash": "^4.17.4",
"rc-util": "^4.6.0",
"warning": "^4.0.3"
Expand Down
14 changes: 11 additions & 3 deletions src/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ export interface FieldProps {
| React.ReactElement
| ((control: ChildProps, meta: Meta, form: FormInstance) => React.ReactNode);
rules?: Rule[];
/** Set up `dependencies` field. When dependencies field update and current field is touched, will trigger validate rules. */
/**
* Set up `dependencies` field.
* When dependencies field update and current field is touched, will trigger validate rules and render.
*/
dependencies?: NamePath[];
trigger?: string;
validateTrigger?: string | string[];
Expand Down Expand Up @@ -123,7 +126,7 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
namePathList: InternalNamePath[] | null,
info: NotifyInfo,
) => {
const { shouldUpdate } = this.props;
const { shouldUpdate, dependencies = [] } = this.props;
const { getFieldsValue, getFieldError }: FormInstance = this.context;
const values = getFieldsValue();
const namePath = this.getNamePath();
Expand Down Expand Up @@ -169,11 +172,15 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti
default:
/**
* - If `namePath` exists in `namePathList`, means it's related value and should update.
* - If `dependencies` exists in `namePathList`, means upstream trigger update.
* - If `shouldUpdate` provided, use customize logic to update the field
* - else to check if value changed
*/
if (
(namePathList && containsNamePath(namePathList, namePath)) ||
dependencies.some(dependency =>
containsNamePath(namePathList, getNamePath(dependency)),
) ||
(shouldUpdate ? shouldUpdate(prevStore, values, info) : prevValue !== curValue)
) {
this.forceUpdate();
Expand Down Expand Up @@ -327,7 +334,8 @@ class Field extends React.Component<FieldProps, FieldState> implements FieldEnti

const { child, isFunction } = this.getOnlyChild(children);
if (!child) {
return children;
// Return origin `children` if is not a function
return isFunction ? child : children;
}

// Not need to `cloneElement` since user can handle this in render function self
Expand Down
6 changes: 3 additions & 3 deletions src/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ export class FormStore {
onValuesChange(changedValues, this.store);
}

this.triggerOnFieldsChange([namePath]);
this.triggerOnFieldsChange([namePath, ...childrenFields]);
};

// Let all child Field get update.
Expand Down Expand Up @@ -365,8 +365,8 @@ export class FormStore {
if (!children.has(field)) {
children.add(field);

if (field.isFieldTouched()) {
const fieldNamePath = field.getNamePath();
const fieldNamePath = field.getNamePath();
if (field.isFieldTouched() && fieldNamePath.length) {
childrenFields.push(fieldNamePath);
fillChildren(fieldNamePath);
}
Expand Down
5 changes: 5 additions & 0 deletions tests/common/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import timeout from './timeout';
import InfoField from './InfoField';

export async function changeValue(wrapper, value) {
wrapper.find('input').simulate('change', { target: { value } });
Expand All @@ -17,3 +18,7 @@ export function matchError(wrapper, error) {
expect(wrapper.find('.errors li').text()).toBe(error);
}
}

export function getField(wrapper, index = 0) {
return wrapper.find(InfoField).at(index);
}
33 changes: 33 additions & 0 deletions tests/dependencies.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { mount } from 'enzyme';
import Form from '../src';
import InfoField from './common/InfoField';
import { changeValue, matchError, getField } from './common';

describe('dependencies', () => {
it('touched', async () => {
let form = null;

const wrapper = mount(
<div>
<Form
ref={instance => {
form = instance;
}}
>
<InfoField name="field_1" />
<InfoField name="field_2" rules={[{ required: true }]} dependencies={['field_1']} />
</Form>
</div>,
);

// Not trigger if not touched
await changeValue(getField(wrapper, 0), '');
matchError(getField(wrapper, 1), false);

// Trigger if touched
form.setFields([{ name: 'field_2', touched: true }]);
await changeValue(getField(wrapper, 0), '');
matchError(getField(wrapper, 1), true);
});
});