How to correctly implement nested dynamic Form in React

Viewed 367

I am trying to implement a nested dynamic form in using React class component with the following JSON output:

"basketData": [
        {
            "finalTransfer": "",
            "averageDistance": ""
            "shares": [
                "share01": "6",
                "share02": "10",
            ]
        },
        {
            "finalTransfer": "",
            "averageDistance": ""
            "shares": [
                "share01": "21",
                "share02": "18",
                "share03": "7",
                "share04": "32",
            ]
        }
]

I want to be able to add multiple basketData dynamically via the form as well as add shares fields dynamically in each basketData.

Here is what my test GUI looks like: enter image description here

Following few examples I have the implementation below so far, however the JSON output is incorrect.

class Step3a extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      baskets: [{ finalTransfer: "", averageDistance: "" }],
      shares: [{ share: "" }],
    };
  }

  handleChange = (index, event) => {
    const { name, value } = event.target;
    const values = [...this.state.baskets];
    values[index][name] = value;
    this.setState({ values });
  };

  addBasket = () => {
    this.setState((prevState) => ({
      baskets: [
        ...prevState.baskets,
        { finalTransfer: "", averageDistance: "" },
      ],
    }));
  };

  removeBasket = (index, event) => {
    let baskets = [...this.state.baskets];
    baskets.splice(index, 1);
    this.setState({ baskets });
  };

  handleShareChange = (idx) => (event) => {
    const newShares = this.state.shares.map((share, sidx) => {
      if (idx !== sidx) return share;
      return { ...share, name: event.target.value };
    });

    this.setState({ shares: newShares });
  };

  handleAddShare = () => {
    this.setState({
      shares: this.state.shares.concat([{ name: "" }]),
    });
  };

  handleRemoveShare = (idx) => () => {
    this.setState({
      shares: this.state.shares.filter((s, sidx) => idx !== sidx),
    });
  };

  isValidated() {
    return true;
  }
  render() {
    const { classes } = this.props;
    let { baskets, shares } = this.state;

    return (
      <GridContainer justify="center">
        <GridItem xs={12} sm={12}>
          <h4 className={classes.infoText}>How about you add a baskets</h4>
        </GridItem>
        <GridItem xs={12} sm={12}>
          {baskets.map((val, idx) => {
            return (
              <Card>
                <CardContent>
                  <GridItem xs={12} sm={12}>
                    <h4>BasketData {idx + 1}</h4>
                  </GridItem>
                  <GridItem xs={12} sm={4}>
                    <TextField
                      fullWidth={true}
                      name="finalTransfer"
                      label="Final Transfer"
                      value={val.finalTransfer}
                      onChange={(event) => this.handleChange(idx, event)}
                    />
                  </GridItem>
                  <GridItem xs={12} sm={4}>
                    <TextField
                      fullWidth={true}
                      name="averageDistance"
                      label="Average Distance"
                      value={val.averageDistance}
                      onChange={(event) => this.handleChange(idx, event)}
                    />
                  </GridItem>
                  <Card>
                    <CardContent>
                      {shares.map((share, index) => {
                        return (
                          <GridItem xs={12} sm={4}>
                            <input
                              type="text"
                              placeholder={`Share #${index + 1} name`}
                              value={share.name}
                              onChange={this.handleShareChange(index)}
                            />
                            <button
                              type="button"
                              onClick={this.handleRemoveShare(index)}
                              className="small"
                            >
                              Remove Share
                            </button>
                          </GridItem>
                        );
                      })}
                      <GridItem>
                        <button
                          type="button"
                          onClick={this.handleAddShare}
                          className="small"
                        >
                          Add Share
                        </button>
                      </GridItem>
                    </CardContent>
                  </Card>
                  <GridItem xs={12} sm={4}>
                    <Button
                      color="rose"
                      className={"Submit"}
                      onClick={(event) => this.removeBasket(idx, event)}
                    >
                      Remove BasketData
                    </Button>
                  </GridItem>
                </CardContent>
              </Card>
            );
          })}
        </GridItem>
        <GridItem xs={12} sm={4}>
        <Button color="rose" className={"Submit"} onClick={this.addBasket}>
            Add BasketData
          </Button>
          </GridItem>
      </GridContainer>
    );
  }
}

Step3a.propTypes = {
  classes: PropTypes.object,
};

export default withStyles(style)(Step3a);

With the above implementation, I get a the output below for a 1 basketData which unfortunately does not meet my need.

"basketData": [
        {
            "basketData": [
                {
                    "finalTransfer": "",
                    "averageDistance": ""
                }
            ],
            "shares": [
                {
                    "share": 0
                },
                {
                    "share": 0
                }
            ],
            "finalTransfer": "100",
            "averageDistance": "180"
        }
]

How can I implement it correctly ?

1 Answers

Seems like you forgot a key to the list. That's why you get duplicated data in your list.

You need to add a unique key to each item if you map an array like this: but try not to use the index but instead a unique id, maybe autogenerated to handle deletes.

Here is a working sandbox of your code.

Related