diff --git a/examples/StateForm-basic.tsx b/examples/StateForm-basic.tsx index c6745840..9835622a 100644 --- a/examples/StateForm-basic.tsx +++ b/examples/StateForm-basic.tsx @@ -38,7 +38,8 @@ export default class Demo extends React.Component { {(control, meta, context) => { const { username } = context.getFieldsValue(); - return username === '111' ? : null; + console.log('my render!', username); + return username === '111' && ; }} diff --git a/src/Field.tsx b/src/Field.tsx index 654847ca..90025dd3 100644 --- a/src/Field.tsx +++ b/src/Field.tsx @@ -169,6 +169,20 @@ class Field extends React.Component implements FieldEnti break; } + case 'dependenciesUpdate': { + /** + * Trigger when marked `dependencies` updated. Related fields will all update + */ + const dependencyList = dependencies.map(getNamePath); + if ( + (namePathList && containsNamePath(namePathList, namePath)) || + dependencyList.some(dependency => containsNamePath(info.relatedFields, dependency)) + ) { + this.forceUpdate(); + } + break; + } + default: /** * - If `namePath` exists in `namePathList`, means it's related value and should update. diff --git a/src/interface.ts b/src/interface.ts index cd8ccf1d..cda75c70 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -97,6 +97,15 @@ export type NotifyInfo = | { type: 'setField'; data: FieldData; + } + | { + type: 'dependenciesUpdate'; + /** + * Contains all the related `InternalNamePath[]`. + * a <- b <- c : change `a` + * relatedFields=[a, b, c] + */ + relatedFields: InternalNamePath[]; }; export interface Callbacks { diff --git a/src/useForm.ts b/src/useForm.ts index fa6bf91a..978fbb00 100644 --- a/src/useForm.ts +++ b/src/useForm.ts @@ -316,6 +316,11 @@ export class FormStore { const childrenFields = this.getDependencyChildrenFields(namePath); this.validateFields(childrenFields); + this.notifyObservers(prevStore, childrenFields, { + type: 'dependenciesUpdate', + relatedFields: [namePath, ...childrenFields], + }); + // trigger callback function const { onValuesChange } = this.callbacks; diff --git a/tests/common/InfoField.tsx b/tests/common/InfoField.tsx index e6a02d47..fbb890f9 100644 --- a/tests/common/InfoField.tsx +++ b/tests/common/InfoField.tsx @@ -6,6 +6,8 @@ interface InfoFieldProps extends FieldProps { children: ReactElement; } +export const Input = ({ value = '', ...props }) => ; + /** * Return a wrapped Field with meta info */ @@ -13,11 +15,7 @@ const InfoField: React.FC = ({ children, ...props }) => ( {(control, { errors }) => (
- {children ? ( - React.cloneElement(children, control) - ) : ( - - )} + {children ? React.cloneElement(children, control) : }
    {errors.map(error => (
  • {error}
  • diff --git a/tests/common/index.js b/tests/common/index.js index 477f5174..e87d58cd 100644 --- a/tests/common/index.js +++ b/tests/common/index.js @@ -1,5 +1,5 @@ import timeout from './timeout'; -import InfoField from './InfoField'; +import InfoField, { Input } from './InfoField'; export async function changeValue(wrapper, value) { wrapper.find('input').simulate('change', { target: { value } }); @@ -22,3 +22,7 @@ export function matchError(wrapper, error) { export function getField(wrapper, index = 0) { return wrapper.find(InfoField).at(index); } + +export function getInput(wrapper, index = 0) { + return wrapper.find(Input).at(index); +} diff --git a/tests/dependencies.test.js b/tests/dependencies.test.js index 1033c60b..3351afa2 100644 --- a/tests/dependencies.test.js +++ b/tests/dependencies.test.js @@ -1,8 +1,8 @@ import React from 'react'; import { mount } from 'enzyme'; -import Form from '../src'; -import InfoField from './common/InfoField'; -import { changeValue, matchError, getField } from './common'; +import Form, { Field } from '../src'; +import InfoField, { Input } from './common/InfoField'; +import { changeValue, matchError, getField, getInput } from './common'; describe('dependencies', () => { it('touched', async () => { @@ -30,4 +30,43 @@ describe('dependencies', () => { await changeValue(getField(wrapper, 0), ''); matchError(getField(wrapper, 1), true); }); + + it('nest dependencies', async () => { + let form = null; + let rendered = false; + + const wrapper = mount( +
    +
    { + form = instance; + }} + > + + + + + + + + {control => { + rendered = true; + return ; + }} + +
    +
    , + ); + + form.setFields([ + { name: 'field_1', touched: true }, + { name: 'field_2', touched: true }, + { name: 'field_3', touched: true }, + ]); + + rendered = false; + await changeValue(getInput(wrapper), '1'); + + expect(rendered).toBeTruthy(); + }); });