0

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>
        </>
    );

3
  • Why don't you keep all your JSX elements inside the render method instead of using a state ? You can use array.map() to create an array of authorField elements inside return of the functional component. Commented Aug 15, 2021 at 2:19
  • @K.vindi Well, I need to add and remove dynamically, so I've imagined that I need to use state in this case. Am I wrong? Commented Aug 15, 2021 at 2:22
  • Yes, better you move all the JSX elements inside the return. To add and remove the authorField you need to manage a state that contains these authors as an array of objects. Each object can cotain details relevant to the author. In order to add the user details, better you manage form inputs as well. And don't keep JSX elements inside it. Use the following code-snippet, I have given as an example. Commented Aug 15, 2021 at 3:03

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you man, that worked perfectly. Would you mind to explain what I did wrong?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.