user10328862
user10328862

Reputation: 155

Best ways to update state array object using React Hooks?

Actually I have a list of elements, that have to store into an array as react state.

const [data, setData] = useState([{"name": "joy", "age": 25}, {"name": "tom", "age": 41}]);

Somehow I need to update only a particular object, May be name or age.

I am doing in such way. But It seems not good.

    setData(prevState => {
      let obj = prevState.find(o => anycondition);
      if(obj !== undefined) {
        obj.name = "Demo";
      }
      return [...prevState];
    })

is there any other method to update only object from state array using React Hooks?

Upvotes: 0

Views: 2712

Answers (4)

Drew Reese
Drew Reese

Reputation: 202836

Doing this is a state mutation!!

setData(prevState => {
  let obj = prevState.find(o => anycondition);
  if(obj !== undefined) {
    obj.name = "Demo"; // <-- state mutation
  }
  return [...prevState];
})

Should always shallowly copy current state that is being updated

setData(prevState => {
  return prevState.map(el => <condition> ? { // <-- map state to new array
    ...el,        // <-- copy element
    name: "Demo", // <-- write new property
  } : el);
})

If you need to do any calculations within the map callback, give the callback a normal function body and add any logic needed.

setData(prevState => {
  return prevState.map(el => { // <-- map state to new array
    // any mapping logic
    ...

    if (<condition>) {
      // any other logic
      ...
      return {
        ...el,        // <-- copy element
        name: "Demo", // <-- write new property
      }
    } else {
      return el;
    }
  });
})

Upvotes: 4

Liu Lei
Liu Lei

Reputation: 1297

try this way ;)

function mergeState(state, payload) {
  return { ...state, ...payload }
}

const [state, dispatch] = useReducer(mergeState, {
 name: '',
 age: 0,
 data: [{"name": "joy", "age": 25}, {"name": "tom", "age": 41}],
})

// get the data
const data = state.data.map(item => {
  if(obj !== undefined){
     obj.name = "Demo";
  }
  return item
})

// update
dispatch({data})


Upvotes: 0

Victor Molina
Victor Molina

Reputation: 2641

To handle this with more control you can add an id prop to each object.

If you want to change/modify an specific object, just do:

const [data, setData] = useState([
    {   
        id: 1,
        name: "joy",
        age: 25
    },
    {
        id: 2,
        name: "tom", 
        age: 41
    }
]);


const updateItem = (id, attributes) => {
  const index = data.findIndex(item => item.id === id);

  if (index === -1)
    return;
  
  setData(
      [
         ...data.slice(0, index),
         Object.assign({}, data[index], attributes),
         ...data.slice(index + 1)
      ]
  );
}


// You can update the second item as follows
updateItem(2, { age: 50 })

Upvotes: 0

Hovakimyan
Hovakimyan

Reputation: 558

Maybe you want something like this?

here you will loop only once and update data which you want

changeHandler = event => {
    const [name, value] = event.target.name
    setData(prevState => prevState.map(item => {
        if (anycondition) {
            return {
                ...item,
                [name]: value
            }
        }
        return item
    }))
}

If you want to get some id from input too, you can pass it with data sets like here data-id={1}

And then get it from an event like this const id = e.target.dataset.id; in changeHandler function

        <input
          type="text"
          name="name"
          data-id={1}
          value={this.state.data.name.fname}
          onChange={this.changeHandler}
        />

Upvotes: 0

Related Questions