1

I'm new to React and trying to add input fields when button Add Bullet is clicked (sort of a ToDo list functionality).

My component is:

  return (
    <Container>
            {bullet.map((text, i) => {
            return (
              <div className="box">
                <p>Bullet {i+1}</p>
                  <input
                    name="Bullet" placeholder="Enter text"
                    value={text} onChange={(event) => {
                      setBullet([event.target.value])
                    }}/>
                    <button onClick={handleAddClick}>Add Bullet</button>
              </div>
            );
            })}

    </Container>
  );
};

The problem is that the function handleAddClick is called (checked via console.log) but it doesn't add the new input method. I'm using state array to update so it will be helpful is someone can help me in fixing the problem.

handleAddClick:

const handleAddClick = useCallback(() => {
    setBullet([
      ...bullet, 
      'Lorem Ipsum is simply dummy text'
    ])
  }, [ bullet ]);

Update: After modifying the function now the problem is that whenever I start typing in an input box it immediately removes all other input boxed and keep only one. I'm passing the value of event.target.value to onChange function.

1
  • You seem to have missed actually invoking the handleAddClick callback <button onClick={(e) => handleAddClick}>Add Bullet</button>, it should be onClick={(e) => handleAddClick(e)} or more simply onClick={handleAddClick}. Commented Jan 22, 2022 at 8:53

2 Answers 2

1

I believe your original handleAddClick handler just needed to use a functional state update to correctly update from the previous state versus whatever state was closed over in scope.

const handleAddClick = () => {
  setBullet(bullets => [
    ...bullets,
    'Lorem Ipsum is simply dummy text'
  ]);
};

To address the update issue, you are completely replacing the bullet state in the input's onChange handler.

<input
  name="Bullet"
  placeholder="Enter text"
  value={text}
  onChange={(event) => {
    setBullet([event.target.value]) // <-- replaceS the entire array!!
  }}
/>

What you'll want is a handler like the remove handler that updates a specific index.

const onChangeHandler = (index: number, value: string) => {
  setBullet(bullets => bullets.map(
    (bullet, i) => i === index ? value : bullet
  )):
}

...

<input
  name="Bullet"
  placeholder="Enter text"
  value={text}
  onChange={e => onChangeHandler(i, e.target.value)}
/>
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks @Drew but in the onChangeHandler I'm getting errors in index and value " Parameter 'index' implicitly has an 'any' type". I added the type number to index and any to value but it doesn't fix the problem.
@Ashar Hmm, odd, it's not like you've much else typed out in your code. Oh, I see the issue... I wrote the handler like the input's value (a string) was being passed, but it's the onChange event object. Let me adjust solution.
@Ashar Ok, please check updated answer now. Sorry for the confusion.
1

You should wrap your callback handler inside of useCallback() to memoize it. At the moment, your function is being defined every time the component is rendered, so its reference value for bullet is using the initial value, rather than the updated one:

const handleAddClick = useCallback(() => {
  setBullet([
    ...bullet, 
    'Lorem Ipsum is simply dummy text'
  ])
}, [ bullet ]);

1 Comment

Using callback indeed fixed the problem but now whenever I type in an input box it removes all other input boxes. I added the description in my Update. Can you please help me with this?

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.