logoAnt Design

⌘ K
  • Design
  • Development
  • Components
  • Blog
  • Resources
5.25.4
  • CSS in v6
  • 👀 Visual Regression Testing
  • Why is it so hard to disable the date?
  • HOC Aggregate FieldItem
  • Line Ellipsis Calculation
  • 📢 v4 surpassed maintenance period
  • Type Util
  • A build ghost
  • Ant Design meets CSS Variables
  • Historical Debt of API
  • Stacked Notification
  • Color Models and Color Picker
  • Extends Theme
  • Virtual Table is here!
  • Happy Work Theme
  • Where is the dynamic style?
  • Suspense breaks styles
  • Bundle Size Optimization
  • Hi, GitHub Actions
  • To be what you see
  • Pain of static methods
  • SSR Static style export
  • Dependency troubleshooting
  • Contributor development maintenance guide
  • Repost: How to submit a riddle
  • Tooltip align update
  • Unnecessary Rerender
  • How to Grow as a Collaborator
  • Funny Modal hook BUG
  • about antd test library migration
  • Tree's check conduction
  • Some change on getContainer
  • Component-level CSS-in-JS
Encapsulating Aggregate Field Components
Approach Summary
getValueProps
getValueFromEvent
transform
Final Result
Summary

HOC Aggregate FieldItem

2024-04-26
@crazyair
contributors
  • Why is it so hard to disable the date?Line Ellipsis Calculation

    Resources

    Ant Design X
    Ant Design Charts
    Ant Design Pro
    Pro Components
    Ant Design Mobile
    Ant Design Mini
    Ant Design Web3
    Ant Design Landing-Landing Templates
    Scaffolds-Scaffold Market
    Umi-React Application Framework
    dumi-Component doc generator
    qiankun-Micro-Frontends Framework
    Ant Motion-Motion Solution
    China Mirror 🇨🇳

    Community

    Awesome Ant Design
    Medium
    Twitter
    yuque logoAnt Design in YuQue
    Ant Design in Zhihu
    Experience Cloud Blog
    seeconf logoSEE Conf-Experience Tech Conference

    Help

    GitHub
    Change Log
    FAQ
    Bug Report
    Issues
    Discussions
    StackOverflow
    SegmentFault

    Ant XTech logoMore Products

    yuque logoYuQue-Document Collaboration Platform
    AntV logoAntV-Data Visualization
    Egg logoEgg-Enterprise Node.js Framework
    Kitchen logoKitchen-Sketch Toolkit
    Galacean logoGalacean-Interactive Graphics Solution
    xtech logoAnt Financial Experience Tech
    Theme Editor
    Made with ❤ by
    Ant Group and Ant Design Community
    loading

    During the form development process, there are occasional needs for combining attributes. The UI display fields are different from the backend data structure fields. For example, when interfacing with the backend, the province and city fields are often defined as two separate fields { province: Beijing, city: Haidian }, rather than a combined one { province: [Beijing, Haidian] }. Therefore, it is necessary to handle the values in initialValues and onFinish as follows:

    tsx
    import React from 'react';
    import { Cascader, Form } from 'antd';
    const data = { province: 'Beijing', city: 'Haidian' };
    const options = [
    { value: 'zhejiang', label: 'Zhejiang', children: [{ value: 'hangzhou', label: 'Hangzhou' }] },
    { value: 'jiangsu', label: 'Jiangsu', children: [{ value: 'nanjing', label: 'Nanjing' }] },
    ];
    const createUser = (values) => console.log(values);
    const Demo = () => (
    <Form
    initialValues={{ province: [data.province, data.city] }}
    onFinish={(values) => {
    const { province, ...rest } = values;
    createUser({ province: province[0], city: province[1], ...rest });
    }}
    >
    <Form.Item label="Address" name="province">
    <Cascader options={options} placeholder="Please select" />
    </Form.Item>
    </Form>
    );
    export default Demo;

    Encapsulating Aggregate Field Components

    When the form is relatively simple, it's manageable, but when encountering a Form.List scenario, it becomes necessary to process the values using map, which can become quite complex. Therefore, we need to encapsulate an aggregated field component to enable a single Form.Item to handle multiple name attributes.

    Approach Summary

    To implement the aggregation field functionality, we need to utilize getValueProps, getValueFromEvent, and transform to facilitate the transformation of data from FormStore and to re-insert the structure into FormStore upon change.

    getValueProps

    By default, Form.Item passes the field value from FormStore as the value prop to the child component. However, with getValueProps, you can customize the props that are passed to the child component to implement transformation functionality. In an aggregation scenario, we can iterate through names and combine the values from FormStore into a single value that is then passed to the child component:

    tsx
    getValueProps={() => ({ value: names.map((name) => form.getFieldValue(name)) })}

    getValueFromEvent

    When the child component modifies the value, the setFields method is used to set the aggregated value returned by the child component to the corresponding name, thereby updating the values of names in FormStore:

    tsx
    getValueFromEvent={(values) => {
    form.setFields(names.map((name, index) => ({ name, value: values[index] })));
    return values[0];
    }}

    transform

    In rules, the default provided value for validation originates from the value passed to the corresponding name when the child component changes. Additionally, it is necessary to retrieve the values of names from FormStore and use the transform method to modify the value of rules:

    tsx
    rules={[{
    transform: () => {
    const values = names.map((name) => form.getFieldValue(name));
    return values;
    },
    }]}

    Final Result

    Summary

    By doing so, we have implemented a feature that allows for operating multiple names within a Form.Item, making the form logic clearer and easier to maintain.

    In addition to the Cascader example in the text, it is also applicable to components such as DatePicker.RangePicker. In other words, this method can be used in any scenario that requires the aggregation of multiple fields.

    Additionally, there are some edge cases in this example that have not been considered. For instance, setFields([{ name:'city', value:'nanjing' }]) will not update the selected value of Cascader. To achieve a refresh effect, you need to add Form.useWatch(values => resetNames.map(name => get(values, name)), form);.

    Feel free to explore more edge cases and handle them as needed.

    CodeSandbox Icon
    codeblock
    codepen icon
    External Link Icon
    expand codeexpand code
    Beijing / Haidian
    Please select