Correct modification of state arrays in React.js

JavascriptReactjs

Javascript Problem Overview


I want to add an element to the end of a state array, is this the correct way to do it?

this.state.arrayvar.push(newelement);
this.setState({ arrayvar:this.state.arrayvar });

I'm concerned that modifying the array in-place with push might cause trouble - is it safe?

The alternative of making a copy of the array, and setStateing that seems wasteful.

Javascript Solutions


Solution 1 - Javascript

The React docs says:

> Treat this.state as if it were immutable.

Your push will mutate the state directly and that could potentially lead to error prone code, even if you are "resetting" the state again afterwards. F.ex, it could lead to that some lifecycle methods like componentDidUpdate won’t trigger.

The recommended approach in later React versions is to use an updater function when modifying states to prevent race conditions:

this.setState(prevState => ({
  arrayvar: [...prevState.arrayvar, newelement]
}))

The memory "waste" is not an issue compared to the errors you might face using non-standard state modifications.

Alternative syntax for earlier React versions

You can use concat to get a clean syntax since it returns a new array:

this.setState({ 
  arrayvar: this.state.arrayvar.concat([newelement])
})

In ES6 you can use the Spread Operator:

this.setState({
  arrayvar: [...this.state.arrayvar, newelement]
})

Solution 2 - Javascript

Easiest, if you are using ES6.

initialArray = [1, 2, 3];

newArray = [ ...initialArray, 4 ]; // --> [1,2,3,4]

New array will be [1,2,3,4]

to update your state in React

this.setState({
  arrayvar:[...this.state.arrayvar, newelement]
});

Learn more about array destructuring

Solution 3 - Javascript

The simplest way with ES6:

this.setState(prevState => ({
    array: [...prevState.array, newElement]
}))

Solution 4 - Javascript

React may batch updates, and therefore the correct approach is to provide setState with a function that performs the update.

For the React update addon, the following will reliably work:

this.setState( state => update(state, {array: {$push: [4]}}) );

or for concat():

this.setState( state => ({
    array: state.array.concat([4])
}));

The following shows what https://jsbin.com/mofekakuqi/7/edit?js,output as an example of what happens if you get it wrong.

The setTimeout() invocation correctly adds three items because React will not batch updates within a setTimeout callback (see https://groups.google.com/d/msg/reactjs/G6pljvpTGX0/0ihYw2zK9dEJ).

The buggy onClick will only add "Third", but the fixed one, will add F, S and T as expected.

class List extends React.Component {
  constructor(props) {
    super(props);
    
    this.state = {
      array: []
    }
    
    setTimeout(this.addSome, 500);
  }
  
  addSome = () => {
      this.setState(
        update(this.state, {array: {$push: ["First"]}}));
      this.setState(
        update(this.state, {array: {$push: ["Second"]}}));
      this.setState(
        update(this.state, {array: {$push: ["Third"]}}));
    };

  addSomeFixed = () => {
      this.setState( state => 
        update(state, {array: {$push: ["F"]}}));
      this.setState( state => 
        update(state, {array: {$push: ["S"]}}));
      this.setState( state => 
        update(state, {array: {$push: ["T"]}}));
    };



  render() {

    const list = this.state.array.map((item, i) => {
      return <li key={i}>{item}</li>
    });
       console.log(this.state);

    return (
      <div className='list'>
        <button onClick={this.addSome}>add three</button>
        <button onClick={this.addSomeFixed}>add three (fixed)</button>
        <ul>
        {list}
        </ul>
      </div>
    );
  }
};


ReactDOM.render(<List />, document.getElementById('app'));

Solution 5 - Javascript

If you are using functional components in React

const [cars, setCars] = useState([{
  name: 'Audi',
  type: 'sedan'
}, {
  name: 'BMW',
  type: 'sedan'
}])

...

const newCar = {
  name: 'Benz',
  type: 'sedan'
}

const updatedCarsArray = [...cars, newCar];

setCars(updatedCarsArray);

Solution 6 - Javascript

As @nilgun mentioned in the comment, you can use the react immutability helpers. I've found this to be super useful.

From the docs:

Simple push

var initialArray = [1, 2, 3];
var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]

initialArray is still [1, 2, 3].

Solution 7 - Javascript

Currently so many people facing problem to update the useState hook state. I use this approach to update it safely and wanted to share it here.

This is my state

const [state, setState] = useState([])

Suppose I have a object name obj1 and I want it to append in my state. I will suggest to do it like this

setState(prevState => [...prevState, obj1])

This will safely insert the object at the end and also keep the state consistency

Solution 8 - Javascript

If you are using functional component please use this as below.

const [chatHistory, setChatHistory] = useState([]); // define the state

const chatHistoryList = [...chatHistory, {'from':'me', 'message':e.target.value}]; // new array need to update
setChatHistory(chatHistoryList); // update the state

Solution 9 - Javascript

For added new element into the array, push() should be the answer.

For remove element and update state of array, below code works for me. splice(index, 1) can not work.

const [arrayState, setArrayState] = React.useState<any[]>([]);
...

// index is the index for the element you want to remove
const newArrayState = arrayState.filter((value, theIndex) => {return index !== theIndex});
setArrayState(newArrayState);
 

Solution 10 - Javascript

Here's a 2020, Reactjs Hook example that I thought could help others. I am using it to add new rows to a Reactjs table. Let me know if I could improve on something.

Adding a new element to a functional state component:

Define the state data:

    const [data, setData] = useState([
        { id: 1, name: 'John', age: 16 },
        { id: 2, name: 'Jane', age: 22 },
        { id: 3, name: 'Josh', age: 21 }
    ]);

Have a button trigger a function to add a new element

<Button
    // pass the current state data to the handleAdd function so we can append to it.
    onClick={() => handleAdd(data)}>
    Add a row
</Button>
function handleAdd(currentData) {

        // return last data array element
        let lastDataObject = currentTableData[currentTableData.length - 1]

        // assign last elements ID to a variable.
        let lastID = Object.values(lastDataObject)[0] 

        // build a new element with a new ID based off the last element in the array
        let newDataElement = {
            id: lastID + 1,
            name: 'Jill',
            age: 55,
        }

        // build a new state object 
        const newStateData = [...currentData, newDataElement ]

        // update the state
        setData(newStateData);

        // print newly updated state
        for (const element of newStateData) {
            console.log('New Data: ' + Object.values(element).join(', '))
        }

}

Solution 11 - Javascript

Since you are not allowed to mutate the state directly, you cannot simply push an item to an array.

state = {
      value: '',
      list: ['a', 'b', 'c'],
    };

this.setState({
  list: [...this.state.list, newelement]
})


//or

  onAddItem = () => {
    // not allowed AND not working
    this.setState(state => {
      const list = state.list.push(state.value);
 
      return {
        list,
        value: '',
      };
    });
  };

know more

Solution 12 - Javascript

this.setState(preState=>({arrayvar:[...prevState.arrayvar,newelement]}))

this will work to solve this problem.

Solution 13 - Javascript

What I do is update a value outside the state and do a forceupdate(), the less stuff managed by react the better since you have more control over what is updated. Also creating a new array for every update may be too expensive if the updates are fast

Solution 14 - Javascript

I am trying to push value in an array state and set value like this and define state array and push value by map function.

 this.state = {
        createJob: [],
        totalAmount:Number=0
    }


 your_API_JSON_Array.map((_) => {
                this.setState({totalAmount:this.state.totalAmount += _.your_API_JSON.price})
                this.state.createJob.push({ id: _._id, price: _.your_API_JSON.price })
                return this.setState({createJob: this.state.createJob})
            })

Solution 15 - Javascript

I was having a similar issue when I wanted to modify the array state while retaining the position of the element in the array

This is a function to toggle between like and unlike:

    const liker = (index) =>
		setData((prevState) => {
			prevState[index].like = !prevState[index].like;
			return [...prevState];
		});

as we can say the function takes the index of the element in the array state, and we go ahead and modify the old state and rebuild the state tree

Solution 16 - Javascript

//get the value you want to add
const valor1 = event.target.elements.valor1.value;

//add in object 
        const todo = {
            valor1,
        }

//now you just push the new value into the state

//prevlista is the value of the old array before updating, it takes the old array value makes a copy and adds a new value

setValor(prevLista =>{
return prevLista.concat(todo) })
     

Solution 17 - Javascript

This worked for me to add an array within an array

this.setState(prevState => ({
    component: prevState.component.concat(new Array(['new', 'new']))
}));

Solution 18 - Javascript

//------------------code is return in typescript 

const updateMyData1 = (rowIndex:any, columnId:any, value:any) => {

	setItems(old => old.map((row, index) => {
		if (index === rowIndex) {
		return Object.assign(Object.assign({}, old[rowIndex]), { [columnId]: value });
	}
	return row;
}));

Solution 19 - Javascript

This code work for me:

fetch('http://localhost:8080')
  .then(response => response.json())
  .then(json => {
    this.setState({mystate: this.state.mystate.push.apply(this.state.mystate, json)})
  })

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionfadedbeeView Question on Stackoverflow
Solution 1 - JavascriptDavid HellsingView Answer on Stackoverflow
Solution 2 - JavascriptStateLessView Answer on Stackoverflow
Solution 3 - JavascriptRiddView Answer on Stackoverflow
Solution 4 - JavascriptNealeUView Answer on Stackoverflow
Solution 5 - JavascriptcoderpcView Answer on Stackoverflow
Solution 6 - JavascriptClarkieView Answer on Stackoverflow
Solution 7 - JavascriptmoshfiqronyView Answer on Stackoverflow
Solution 8 - JavascriptHarshan MorawakaView Answer on Stackoverflow
Solution 9 - JavascriptJaggerView Answer on Stackoverflow
Solution 10 - JavascriptIan SmithView Answer on Stackoverflow
Solution 11 - JavascriptMD SHAYONView Answer on Stackoverflow
Solution 12 - Javascriptraja saadView Answer on Stackoverflow
Solution 13 - JavascriptmeiserView Answer on Stackoverflow
Solution 14 - JavascriptRajnikant LodhiView Answer on Stackoverflow
Solution 15 - JavascriptL AndyView Answer on Stackoverflow
Solution 16 - JavascriptFernando AlcantaraView Answer on Stackoverflow
Solution 17 - JavascriptMadKadView Answer on Stackoverflow
Solution 18 - JavascriptANUBHAV RAWATView Answer on Stackoverflow
Solution 19 - JavascriptHamid HosseinpourView Answer on Stackoverflow