1

When a new drum is added to the array of drums in setDrums each drum component is displayed, the user can give the drum a name, how do I add the name to the drum in the array drums?

I can logout the drum id, but how do I find that drum in the array and only update that drum with the name the user entered?

https://codesandbox.io/s/amazing-dan-dsudq?file=/src/index.js:0-1371

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const rootElement = document.getElementById("root");

const Drum = ({ id, count, remove, editName, name }) => {
  const [sounds, setSounds] = useState(count);

  useEffect(() => {
    if (sounds > 0)
      setTimeout(() => {
        console.log("Playing sound");
        setSounds(sounds - 1);
      }, 1000);
  }, [sounds]);

  return (
    <div>
      <p>Drum #{id}</p>
      <p>Drum Name {name}</p>
      <p>Remaining sounds: {sounds}</p>
      <label>
        Drum Name <input type="text" onChange={editName} />
      </label>
      <br />
      <button onClick={remove}>Delete drum #{id}</button>
    </div>
  );
};

const App = () => {
  const [drums, setDrums] = useState([]);
  const [nextId, setNextId] = useState(0);

  return (
    <div>
      {drums.map(drum => (
        <Drum
          key={drum.id}
          id={drum.id}
          count={drum.count}
          remove={() => setDrums(drums.filter(other => drum.id !== other.id))}
          editName={() => console.log(drum.id)} // <== how to save the entered name to setDrums?
          name={drum.name}
        />
      ))}
      <button
        onClick={() => {
          setDrums([
            ...drums,
            { id: nextId, count: Math.floor(Math.random() * 100) }
          ]);
          setNextId(nextId + 1);
        }}
      >
        Add drum
      </button>
    </div>
  );
};

ReactDOM.render(<App />, rootElement);

4 Answers 4

2

Updated

I have updated the codesnadbox also.Link

Theory - To update the value we need an identifier of the array, which is the index of each drum.

I created an editDrumName function which accepts two parameters one is the event and the other is the id. Then I cloned the drums in a tempDrums variable and updated the value with the id.

You cannot do this to the child component as because the value passed in the props.

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const rootElement = document.getElementById("root");

const Drum = ({ id, count, remove, editName, name, index }) => {
  const [sounds, setSounds] = useState(count);

  useEffect(() => {
    if (sounds > 0)
      setTimeout(() => {
        console.log("Playing sound");
        setSounds(sounds - 1);
      }, 1000);
  }, [sounds]);

  return (
    <div>
      <p>Drum #{id}</p>
      <p>Drum Name: {name}</p>
      <p>Remaining sounds: {sounds}</p>
      <label>
        Drum Name <input type="text" onChange={e => editName(e, index)} />
      </label>
      <br />
      <button onClick={remove}>Delete drum #{id}</button>
    </div>
  );
};

const App = () => {
  const [drums, setDrums] = useState([]);
  const [nextId, setNextId] = useState(0);

  const editDrumName = (event, id) => {
    let tempDrums = drums;
    tempDrums[id].name = event.target.value;
    setDrums([...tempDrums]);
  };

  return (
    <div>
      {drums.map((drum, index) => (
        <Drum
          key={drum.id}
          id={drum.id}
          index-{index}
          count={drum.count}
          remove={() => setDrums(drums.filter(other => drum.id !== other.id))}
          editName={editDrumName} // <== how to save the entered name to setDrums?
          name={drum.name}
        />
      ))}
      <button
        onClick={() => {
          setDrums([
            ...drums,
            { id: nextId, count: Math.floor(Math.random() * 100) }
          ]);
          setNextId(nextId + 1);
        }}
      >
        Add drum
      </button>
    </div>
  );
};

ReactDOM.render(<App />, rootElement);
Sign up to request clarification or add additional context in comments.

12 Comments

@Bill This isn't a very correct solution, it mutates the state. Although it clones it later it will cause issues when you try to compare fields like drum.name within previous and current state
hmm ok I'm all ears, can you edit the sand box to show how to do this without the mutation?
@Bill Here is a suggestion for you. Never use numeric values for the id. Try to use UUID instead. numeric values as an id and passing it as a key always create a problem.
Thank you, Md. Moshfiqur Rahman Rony il make this change on your recomendation
you can read this. This helped me a lot - stackoverflow.com/a/46735689/9418800
|
0

What about changing the array to an object then call the setDrums like this:

editName={(e) => setDrums({...drums, drums[drum.id].name: e.target.value )}

1 Comment

Use object will lose order of the list.
0

Since you can't edit the original array directly because react state should be treat as immutable, you should make a new referance of the array on every change.

editName={event => {
  // set the new value to drum
  drum.name = event.target.value;
  // update drums state by creating shallow copy of the old one
  setDrums(drums.slice(0));
}}

ref: http://facebook.github.io/react/docs/component-api.html

Comments

0

The correct way is to map thru the array, find the drum and update it.

We shouldn't mutate state directly. With objects, when we copy it and update a property of copied object, original object is mutated.

working demo is here

Like this

...
const editName = (e, id) => {
    setDrums(
      drums.map(item => {
        if (item.id === id) {
          return { ...item, name: e.target.value };
        }
        return item;
      })
    );
  };
...
    {drums.map(drum => (
        <Drum
          key={drum.id}
          id={drum.id}
          count={drum.count}
          remove={() => setDrums(drums.filter(other => drum.id !== other.id))}
          editName={e => editName(e, drum.id)} // <== how to save the entered name to setDrums?
          name={drum.name}
        />
      ))}
   {drums.map(drum => (
        <Drum
          key={drum.id}
          id={drum.id}
          count={drum.count}
          remove={() => setDrums(drums.filter(other => drum.id !== other.id))}
          editName={e => editName(e, drum.id)} // <== how to save the entered name to setDrums?
          name={drum.name}
        />
      ))}
...

Comments

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.