Keep Ant Design Form Coupling Declarative with dependencies and shouldUpdate
Use Ant Design Form.Item dependencies for cross-field validation and a narrow shouldUpdate render prop for conditional billing fields instead of imperative resets. Keep coupling declarative and verify preserve behavior in onFinish.
17 Jul 2026, 15:23 UTC

The checkout problem that keeps coming back
A checkout form needs two things at once. Billing state is required only when the billing country is US. Billing fields should disappear when the user checks "Same as shipping". The tempting fix is imperative: watch the checkbox, call form.setFieldsValue to clear billing, and toggle required flags in state. That creates drift between the UI and the form store.
The useful takeaway is to keep coupling inside Ant Design Form itself. Use Form.Item dependencies for validation coupling and a narrow shouldUpdate render prop for conditional rendering. Do not imperatively set or clear values unless you explicitly want a side effect.
Validation coupling with dependencies
Ant Design Form revalidates a field when its dependencies change. Give the dependent Form.Item a dependencies prop and a rules function that reads the form via getFieldValue.
dependencies={['billCountry']} tells the form to re-run validation for billState whenever billCountry changes. The rule can return a validator conditionally. This is declarative: the requirement is a function of other field values, not of component state.
Do not fetch option lists inside a rule. Rules run during validation, not as data loaders. Load options in an onChange handler or an effect keyed to the field value.
Conditional rendering with shouldUpdate
shouldUpdate controls when a Form.Item re-renders. Use it only for conditional rendering, not for general state.
Wrap the conditional block in a noStyle Form.Item and provide a comparison function. A narrow comparison avoids re-rendering on every keystroke in an unrelated field.
shouldUpdate={(prev, next) => prev.sameAsShipping !== next.sameAsShipping}
Return null or the billing fields from the render prop. Avoid giving the wrapper Form.Item a name. A named wrapper becomes a field itself and can cause layout and binding surprises. Use noStyle plus a render prop or fragment.
In large forms, shouldUpdate without a comparison re-renders on every form change. That can become a bottleneck before any network call.
Worked billing address example
The example assumes an Ant Design minor version where Form.Item supports dependencies, shouldUpdate, and preserve. Verify these props against the installed docs and package.json for antd and rc-field-form.
// React component, run in a project with antd installed
import { Form, Select, Checkbox, Input, Button } from 'antd';
export default function CheckoutForm() {
const [form] = Form.useForm();
return (
<Form form={form} onFinish={(values) => console.log('onFinish', values)}>
<Form.Item name="shipCountry" label="Shipping country" rules={[{ required: true }]}>
<Select options={[{value:'US',label:'US'}, {value:'CA',label:'CA'}]} />
</Form.Item>
<Form.Item name="sameAsShipping" valuePropName="checked">
<Checkbox>Same as shipping</Checkbox>
</Form.Item>
<Form.Item noStyle shouldUpdate={(prev, next) => prev.sameAsShipping !== next.sameAsShipping}>
{({ getFieldValue }) => {
if (getFieldValue('sameAsShipping')) return null;
return (
<>
<Form.Item name="billCountry" label="Billing country" rules={[{ required: true }]}>
<Select options={[{value:'US',label:'US'}, {value:'CA',label:'CA'}]} />
</Form.Item>
<Form.Item
name="billState"
label="Billing state"
dependencies={['billCountry']}
rules={[
({ getFieldValue }) => ({
validator(_, value) {
if (getFieldValue('billCountry') === 'US' && !value) {
return Promise.reject(new Error('State is required for US'));
}
return Promise.resolve();
},
}),
]}
>
<Input />
</Form.Item>
</>
);
}}
</Form.Item>
<Form.Item>
<Button htmlType="submit">Submit</Button>
</Form.Item>
</Form>
);
}
The billState rule is evaluated when billCountry changes because of dependencies. The billing block appears only when sameAsShipping is false because of shouldUpdate. No imperative setFieldsValue is used.
Trade-off: preserve and the submit payload
Form.Item preserve defaults to true. Hidden fields often keep values in the form store. That means unchecking sameAsShipping later can reveal stale billing data, and submitting with sameAsShipping checked can still include billCountry and billState in the onFinish values.
Set preserve={false} on fields that must be discarded when hidden, and test error clearing. Validation coupling does not automatically clean the submitted payload. Inspect the final values object in onFinish for the installed Ant Design minor version.
Also verify that a shouldUpdate item does not re-render excessively. React DevTools Profiler while typing in an unrelated field can confirm the comparison is narrow enough.
Actionable close
Implement one dependent rule with dependencies and one conditional block with a narrow shouldUpdate. Add a manual check that toggles sameAsShipping and changes billCountry between US and a non-US value. Assert the required rule appears and clears without unrelated fields losing focus or value. Inspect the onFinish payload to confirm hidden fields are preserved or cleared as intended for your version.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.