Removing complex components from an array in ReactJS

I’m trying to make a list of components. I need to remove the items individually but it seems that the state inside the remove function is always outdated.

For example, if I add 10 authors and click in the 10th author remove button, it’ll show me 9 elements (which is already wrong) and if I click on the 2nd author, it shows me just 1 element inside the array. Am I missing something here?

    const [authorsFields, setAuthorsFields] = useState<Array<JSX.Element>>([]);

    const removeAuthorField = () => {
        console.log(authorsFields.length);
    }

    const removeButton = () => {
        return (
            <Col className={"d-flex justify-content-end py-1"}>
                <Button variant={"danger"} onClick={() => removeAuthorField()}>Remove author</Button>
            </Col>
        );
    }

    const authorField = (removable: boolean) => {
        return (
            <>
                <Row className={"mb-2"}>
                    <Form.Group className={"py-1"}>
                        <Form.Label>Author name</Form.Label>
                        <Form.Control type={"text"}/>
                    </Form.Group>
                    {removable && removeButton()}
                </Row>
            </>
        );
    }

    const addAuthorField = () => {
        if (authorsFields.length !== 0) {
            setAuthorsFields((old) => [...old, authorField(true)]);
        } else {
            setAuthorsFields([authorField(false)]);
        }
    }

    useEffect(() => {
        if (authorsFields.length === 0) {
            addAuthorField();
        }
    }, [])

    return (
        <>
            <Col sm={3} style={{maxHeight: "60vh"}} className={"mt-4"}>
                <Row>
                    {authorsFields}
                    <Row>
                        <Form.Group className={"py-1"}>
                            <Button style={{width: "100%"}} onClick={() => addAuthorField()}>
                                Add Author
                            </Button>
                        </Form.Group>
                    </Row>
                </Row>
            </Col>
        </>
    );

Answer

Use the following functional component as an example to modify your code on how to use JSX elements seperated from the state management inside the functional components.

import React, { useState } from "react";
import { Button, Row, Col } from "antd";

function App() {
  const [authorsCount, setAuthorsCount] = useState(0);

  // Use authorsFields to manage authors details in an array of objects
  const [authorsFields, setAuthorsFields] = useState([]);

  const removeAuthorField = (id) => {
    // To remove relevant author filter out the authors without the relevant id
    setAuthorsFields((old) =>
      old.filter((authorField) => authorField.id !== id)
    );
  };

  const addAuthorField = () => {
    setAuthorsFields((old) => [...old, { id: authorsCount, removable: true }]);
    setAuthorsCount((old) => old + 1);
  };

  return (
    <div>
      <Col sm={3} style={{ maxHeight: "60vh" }} className={"mt-4"}>
        <Row>
          {authorsFields.map((authorField) => (
            <Row className={"mb-2"}>
              <div className={"py-1"}>
                <div>{`Author name ${authorField.id}`}</div>
              </div>
              {authorField.removable && (
                <>
                  <Col className={"d-flex justify-content-end py-1"}>
                    <Button
                      variant={"danger"}
                      onClick={() => removeAuthorField(authorField.id)}
                    >
                      Remove author
                    </Button>
                  </Col>
                </>
              )}
            </Row>
          ))}
          <Row>
            <div className={"py-1"}>
              <Button
                style={{ width: "100%" }}
                onClick={() => addAuthorField()}
              >
                Add Author
              </Button>
            </div>
          </Row>
        </Row>
      </Col>
    </div>
  );
}

export default App;

Following is the view.

enter image description here