Ant Design cannot update initial Value for dynamic Form Item

Viewed 765

I am using Ant Design 4 to make a dynamic form like this: there is an "Add form item" button, when you click it, you can add a select box to your form. After selecting one option from that select box, there will be some inputs with an initial value based on the selected value data. My problem is these inputs fields only render one time when selecting one value from the select box for the first time. I have logged the "item" data inside infoData[0]?.map((item, index), it returns with the right value but the Form Inputs do not update their initialValue. Here is my code:

import React, { useState, useCallback } from "react";
import { Button, Form, Select, Input } from "antd";

const FormPage = () => {
  const [userData, setUserData] = useState([]);
  const [optionList, setOptionList] = useState([
    {
      name: "Option 1",
      value: "o-1",
      info: [
        { name: "Option 1 Info 1", point: 11 },
        { name: "Option 1 Info 2", point: 12 },
      ],
    },
    {
      name: "Option 2",
      value: "o-2",
      info: [
        { name: "Option 2 Info 1", point: 13 },
        { name: "Option 2 Info 2", point: 14 },
      ],
    },
  ]);

  const onSubmitForm = (values: object) => {
    console.log(values);
  };

  const addUserData = () => {
    const newData = {
      id: Math.random().toString(36).substring(7),
      userName: Math.random().toString(36).substring(7),
    };

    setUserData([...userData, newData]);
  };

  const selectedOption = (val) => {
    const selectedData = optionList.filter((item) => item.value === val);
    return selectedData[0].info;
  };

  return (
    <Form onFinish={onSubmitForm}>
      <p>
        <Button onClick={addUserData}>Add Form Item</Button>
      </p>
      {userData.length > 0
        ? userData.map((item, index) => (
            <FormPageItem
              key={index}
              data={item}
              index={index}
              addUserData={addUserData}
              optionList={optionList}
              selectedOption={selectedOption}
            />
          ))
        : ""}
      <Button type="primary">Submit</Button>
    </Form>
  );
};

const FormPageItem = (props) => {
  const [infoData, setInfoData] = useState([]);

  const handleSelectOption = (value) => {
    const selected = props.selectedOption(value);
    console.log("selected", selected);
    const newData = [...infoData];
    newData[0] = selected;
    console.log(newData);
    setInfoData(newData);
  };

  const renderInput = useCallback(() => {
    if (infoData.length > 0) {
      return (
        <>
          {infoData[0]?.map((item, index) => (
            <Form.Item
              name={[`option-${props.data.id}`, `user-info-${index}`]}
              style={{ marginBottom: 16 }}
              initialValue={item.name}
              key={index}
            >
              <Input />
            </Form.Item>
          ))}
        </>
      );
    }
    return "";
  }, [infoData]);

  return (
    <div>
      <Form.Item
        name={[`option-${props.data.id}`, "options"]}
        label="Option List"
      >
        <Select showArrow={false} onChange={handleSelectOption}>
          {props.optionList.map((item) => (
            <Select.Option value={item.value} key={item.value}>
              {item.name}
            </Select.Option>
          ))}
        </Select>
        {renderInput()}
      </Form.Item>
    </div>
  );
};

export default FormPage;
1 Answers

Please see the documents here ant design form documents:

Note that initialValues cannot be updated by setState dynamically, you should use setFieldsValue in that situation.

In your case, you can just replace renderInput function with simple render function, and even no need of onChange function for the first Select component.

    const FormPageItem = (props) => {
      const [infoData, setInfoData] = useState([]);


      return (
        <div>
          <Form.Item
            name={[`option-${props.data.id}`, "options"]}
            label="Option List"
          >
            <Select showArrow={false} >
              {props.optionList.map((item) => (
                <Select.Option value={item.value} key={item.value}>
                  {item.name}
                </Select.Option>
              ))}
            </Select>
            {/* {renderInput()} */}
          </Form.Item>

          <Form.Item
            dependencies={[[`option-${props.data.id}`,"options"]]}
          >
            {({getFieldValue})=>{
              const v = getFieldValue([`option-${props.data.id}`,"options"])

              if(!v){return null}

              const selected = props.selectedOption(v);
      
              return (
                <>
                  {selected?.map((item, index) => (
                    <Form.Item
                      name={[`option-${props.data.id}`, `user-info-${index}`]}
                      style={{ marginBottom: 16 }}
                      initialValue={item.name}
                      key={index}
                      >
                        <Input />
                      </Form.Item>
                   ))}
                 </>
              )
            }}
          </Form.Item>
        </div>
      );
    };
Related