skip to Main Content

I’m using NextJs and React to create a form. I have an input box that gets created with a button click. There can be multiple input boxes. To delete the input box, you click the delete button next to it.

Picture of inputs and buttons

The issue is: if you click Delete on one row, it will delete that row and all the rows after it.

E.g. if you click delete on row 2, it will delete row 2 and three. if you click delete on row 1, it will delete all the rows. If there are 10 rows, and you delete row 4, there will only be 3 rows.

I saw a similar similar Stackoverflow where it said to not use the length because the length kept changing but that does not seem to be the case here. I changed the ID to be a timestamp from new Date.getTime() .

Here’s my parent component:

export default function CmdBuilder() {
  const [cmdNodes, setCmdNodes] = React.useState([]);

  function addRow() {
    setCmdNodes(
      cmdNodes.concat(
        <CmdNode key={new Date().getTime()} uniqueId={new Date().getTime()} onRemove={(id) => removeNode(id)} />
      ));
  }

  function removeNode(id) {
    setCmdNodes((nodes) => cmdNodes.filter((node) => node.uniqueId != id));
    );
  }

  return (
    <div id='cmdBuilder'>
      {cmdNodes}
      <button onClick={addRow}>Add Row</button>
    </div>
  )
}

Here is my child component:

export default function CmdNode(props) {
  return (
    <div className={styles.builderGroup} id={props.uniqueId}>
      <input type='text' className={styles.cmdText} />
      <button className={styles.cmdDelete} onClick={() => props.onRemove(props.uniqueId)}>Delete</button>
    </div>
  )
}

What could I be missing? The logic seems right but please let me know if I’m missing something fundamental.

Edit: I’ve verified that the key and uniqueId values match, if that matters. I’ve also verified that the selected row’s uniqueId is correct.

2

Answers


  1. In your removeNode you are passing a function to setState which is fine. React will pass the component’s current state as a parameter to this function. In your definition you have named this parameter nodes, but then you refer to cmdNodes in the function body instead of nodes. Simply change

    (nodes) => cmdNodes.filter((node) => node.uniqueId != id)
    

    to

    (nodes) => nodes.filter((node) => node.uniqueId != id)
    

    The first version doesn’t work because the value of cmdNodes is bound to the function closure at the time when the function is defined; it’s not actually deleting all the components after it, you’re just accidentally resetting state to the value it had when onRemove was initialized.

    You could probably just set the value of setCmdNodes as well. I haven’t tested it but it would avoid some of this closure nonsense.

    setCmdNodes(cmdNodes.filter((node) => node.uniqueId != id))
    
    Login or Signup to reply.
  2. Don’t put component instances in state, put data in state and render components:

    function CmdBuilder() {
      const [cmdNodes, setCmdNodes] = React.useState([]);
    
      function addRow() {
        setCmdNodes((nodes) => [...nodes, { id: new Date().getTime() }]);
      }
    
      function removeNode(id) {
        setCmdNodes((nodes) => nodes.filter((node) => node.id !== id));
      }
    
      return (
        <div id="cmdBuilder">
          {cmdNodes.map(({ id }) => (
            <CmdNode key={id} uniqueId={id} onRemove={removeNode} />
          ))}
          <button onClick={addRow}>Add Row</button>
        </div>
      );
    }
    
    function CmdNode(props) {
      return (
        <div className={styles.builderGroup} id={props.uniqueId}>
          <input type="text" className={styles.cmdText} />
          <button
            className={styles.cmdDelete}
            onClick={() => props.onRemove(props.uniqueId)}
          >
            Delete
          </button>
        </div>
      );
    }
    

    You also had a typo where you weren’t using nodes in removeNode – look for the unused variable low-lighting in your IDE to catch bugs of that sort.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search