Hi,

React compares by reference, in the setState(newState) call, you are just setting state to the same list. Changing the contents of the list doesn't change the reference to the list. When the next state is the same as the old state, React bails out on doing any work.
One quick thing you can do is to slice the array like:

const newData = oldData.slice(0);
newData[0] = 'something'
setState(newData);

Here's an even more in-depth explanation: https://pavi2410.me/blog/dont-use-usestate-to-handle-arrays-in-react, you don't have to do as they say in the article (mutating and forcing render), but it helps you wrap your head around the issue.

Reactivity by assignment just doesn't work like that in JavaScript, you need a compiler like Svelte does, or you could use a library like https://github.com/immerjs/immer which implements Proxy to achieve reactivity by assignment. Or use getters/setters.

Understanding how JavaScript works is key here, you can just try this out in your browser console:

let arr = [0,1,2,3]
let barr = arr
barr[2] = 100000
barr === arr // true

So React can't tell that it is different data, and diffing the entire list would be also not guaranteed, because the contents of list, could be references themselves (not primitive data such as numbers, strings, etc.)!

Top answer
1 of 4
13

When updating your state using hooks, it's best to use callbacks.

Try updating your code:

setNames(prevNames => [...prevNames, ...newNames])

This works in the same way that prevState worked with setSate().

On to your solution, you can set the initial state within useState():

const array = [
  {
    age: 13,
    name: "Housing"
  },
  {
    age: 23,
    name: "Housing"
  }
];

const [names, setNames] = useState(() => array.map(item => item.name));

useEffect(() => {
  console.log(names);
}, []);
2 of 4
7

You should have the useEffect() subscribe to both the contentLoading and array props, so it runs whenever there is a change to either, instead of running a single-time after the first mount.

Working code and sandbox: https://codesandbox.io/s/mutable-mountain-wgjyv

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

import "./styles.css";

const array = [
  {
    age: 13,
    name: "Housing"
  },
  {
    age: 23,
    name: "Housing"
  }
];

const App = () => {
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    setTimeout(() => {
      setLoading(true);
    }, 2000);
  }, []);

  return <Child array={array} contentLoading={loading} />;
};

const Child = ({ array, contentLoading }) => {
  const [names, setNames] = useState([]);
  useEffect(() => {
    createArray();
  }, [contentLoading, array]);

  const createArray = () => {
    if (contentLoading) {
      const newNames = array.map(item => item.name);
      setNames([...names, ...newNames]);
    }
  };

  return (
    <div>
      {names.map(name => (
        <div>{name}</div>
      ))}
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Discussions

React useState() array not updating
I have created new custom selectbox using React. There is pre-populated array which I am loading on component load (using useEffect). When user search for any non existing country there would be a ... More on stackoverflow.com
🌐 stackoverflow.com
useState not updating an array at all
I'm trying to update the state of an array with React Hooks, using an input received from a child component. This is the code for the array I'm trying to update (in my App.js file): const [results, More on stackoverflow.com
🌐 stackoverflow.com
React useState Help - Update item in Array of Objects
Hello, I’m creating a game where you have to roll ten dice and get their numbers to match. You can reroll the dice and you can click on a die to freeze it (keep it’s number/don’t reroll) I’m storing dice data in a useState that holds an array of objects When I try to update the useState ... More on forum.freecodecamp.org
🌐 forum.freecodecamp.org
0
0
February 8, 2024
Need help with useState hook - can't update an array using the spread operator
Both versions look like they should work -- maybe it's something in the surrounding context? Can you share more and/or repro in a codepen? More on reddit.com
🌐 r/reactjs
9
2
December 5, 2021
🌐
GitHub
github.com › facebook › react › issues › 15041
React Hooks useState updating an array · Issue #15041 · facebook/react
March 6, 2019 - const [messages, setMessages] = useState([]); function receiveMsg(msg) { setMessages(messages.concat(JSON.parse(msg.data))); } useEffect(function() { if (_.isUndefined(socket)) { let ws = new MockWebsocket("ws://127.0.0.1:8080/"); ws.onmessage = receiveMsg; } }); The effect of this is that I only ever get the latest message in my array. Example 2 If, however, I set the onmessage function on every render as in this example, then I get my full array with data appended as I would expect.
Author   brettshollenberger
🌐
DevGenius
blog.devgenius.io › updating-arrays-and-objects-with-usestate-in-react-what-you-may-not-know-4af169c496c0
Updating Arrays and Objects with useState in React: What you May Not Know
January 19, 2024 - The result is that setState will only update the corresponding Object (“John”), changing every property that differs from the previous state value, and including everything that didn’t exist. That Is It for today, I hope ended up learning a little bit more about how to use the useState Hook in React, and also how to copy complex data in Javascript.
Top answer
1 of 3
8

The problem appears to be that you're passing to useState() the array (updatedVal) that has its reference unchanged, thus it appears to React that your data hasn't been modified and it bails out without updating your state.

Try drop that unnecessary variable and do directly setCountries([...countries, obj])

Another minor fix about your code I may suggest: you may use Array.prototype.every() to make sure that every existing item has different label. It has two advantages over .filter() - it will stop looping right upon hitting duplicate (if one exists) and won't proceed till the end of array (as .filter() does), thus won't slow down unnecessarily re-render and it returns boolean, so you won't actually need extra variable for that.

Following is a quick demo of that approach:

const { useState, useEffect } = React,
      { render } = ReactDOM,
      rootNode = document.getElementById('root')
      
const CountryList = () => {
  const [countries, setCountries] = useState([])
  
  useEffect(() => {
    fetch('https://run.mocky.io/v3/40a13c3b-436e-418c-85e3-d3884666ca05')
      .then(res => res.json())
      .then(data => setCountries(data))
  }, [])
  
  const addCountry = e => {
    e.preventDefault()
    const countryName = new FormData(e.target).get('label')
    if(countries.every(({label}) => label != countryName))
      setCountries([
        ...countries,
        {
          label: countryName,
          value: countries.length+1
        }
      ])
    e.target.reset()
  }
  
  return !!countries.length && (
    <div>
      <ul>
        {
          countries.map(({value, label}) => (
            <li key={value}>{label}</li>
          ))
        }
      </ul>
      <form onSubmit={addCountry}>
        <input name="label" />
        <input type="submit" value="Add country" />
      </form>
    </div>
  )
}

render (
  <CountryList />,
  rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>

2 of 3
2

I have found the root problem. In your Select Box component you have:

 const [defaultValueoptions, setdefaultValueoptions] = useState(options);

It should be:

 const [defaultValueoptions, setdefaultValueoptions] = useState([]);

  useEffect(() => {
    setdefaultValueoptions(options);
  }, [options]);
🌐
Techiediaries
techiediaries.com › react-usestate-hook-update-array
Update Arrays with React useState Hook Without Push |
April 24, 2022 - You can't update the array directly without using the method returned from useState().
🌐
TechNetExperts
technetexperts.com › home › react usestate array not updating: a common problem [solved]
React useState array not updating: A common problem [SOLVED]
November 13, 2025 - React useState array not updating? This article will show you how to solve this common problem and use React immutable state to manage state in a way that prevents errors and unexpected behavior.
Find elsewhere
🌐
freeCodeCamp
forum.freecodecamp.org › javascript
React useState Help - Update item in Array of Objects
February 8, 2024 - Hello, I’m creating a game where you have to roll ten dice and get their numbers to match. You can reroll the dice and you can click on a die to freeze it (keep it’s number/don’t reroll) I’m storing dice data in a useState that holds an array of objects When I try to update the useState object by directly reversing the die’s isFrozen boolean, it doesn’t work.
🌐
Reddit
reddit.com › r/reactjs › need help with usestate hook - can't update an array using the spread operator
r/reactjs on Reddit: Need help with useState hook - can't update an array using the spread operator
December 5, 2021 -

I am fairly new to React and I think I am missing an important concept about state.

I am trying to update a state variable, specifically an array. The following function successfully updates the array if I use Array.push(), but not if I use destructuring/the spread operator. What am I missing? Also, the template is completely non-reactive - even when the console.log statement displays the correct value, the HTML does not.

function MyComponent() {
    const [selection, setSelection] = useState([]);

    const handleSelection = (id) => {
        // doesn't work
        // let newSelection = [...selection, id];

        let newSelection = selection;
        newSelection.push(id);

        // Prints the correct value when using Array.push() but not destructuring
        console.log(selection);

        setSelection(newSelection);
    };

    return (
        {/* never changes */}
        <pre>{JSON.stringify(selection, null, 2)}</pre>
    )
}

EDIT: I thought this was a problem with how I was setting state on the selection variable, but it turns out the issue was with other parts of my code that are tangentially related to this. The problem was that I was trying to to loop over some data and generate a component for each datum, but I wasn't doing that correctly. I was setting a different state variable to hold these components, and the various pieces of state were out of sync. Here is a repro of the working code, where I am mapping the components correctly (in the template directly rather than setting state, which seems to be better practice). As you can see, spread/destructuring works perfectly as expected now.

🌐
React
react.dev › learn › updating-arrays-in-state
Updating Arrays in State – React
Updating Objects explains what mutation is and why it’s not recommended for state. push() will mutate an array, which you don’t want: ... import { useState } from 'react'; let nextId = 0; export default function List() { const [name, setName] = useState(''); const [artists, setArtists] = useState([]); return ( <> <h1>Inspiring sculptors:</h1> <input value={name} onChange={e => setName(e.target.value)} /> <button onClick={() => { artists.push({ id: nextId++, name: name, }); }}>Add</button> <ul> {artists.map(artist => ( <li key={artist.id}>{artist.name}</li> ))} </ul> </> ); }
🌐
freeCodeCamp
forum.freecodecamp.org › t › usestate-to-update-an-array-does-not-work › 410244
useState to update an array does not work - The freeCodeCamp Forum
July 17, 2020 - I am trying to update the array using useEffect + useState. const CategorySearch = (props) => { const initialValue = [ { value: '', name: '', }, ]; const [options, setOptions] = useState(initialValue); const updateOptions = () => { let convert = []; for (const options of props.categories) { convert.push({ value: options._id, label: options.name }); } console.log(convert); setOptions(convert); console.log(options); }; useEffect(() ...
🌐
Reddit
reddit.com › r/reactjs › updating array with usestate and rendering with useeffect
r/reactjs on Reddit: updating array with useState and rendering with useEffect
December 10, 2022 -

It's my first personal project and I'm stuck with two problems. I already tried a lot of different things, watched videos, and checked some codes, but still, nothing worked. I feel like it's something very basic that I'm forgetting.

The project is a Notepad app, a very simple one that I intend to add more features to later on.

However, every time my "send" button is clicked, the value doesn't get saved, it only shows up in the array on the second click.

Then, my list of 'Saved Notes' won't render the new note when "send" is clicked, only when I reload the page. I haven't been successful in using useEffect for this.

pages/home.jsx

import NotePad from "../Components/NotePad";
import StickNotes from "../Components/StickNotes";

export default function Home() {
  return (
    <main>
      Note Padding
      <NotePad />
      <StickNotes />
    </main>
  )
}

------

components/NotePad.jsx

import { useState } from 'react';

export default function NotePad() {
  const [title, setTitle] = useState('')
  const [note, setNote] = useState('')
  const [noteList, setNoteList] = useState([])

  function submitNote() {
    const noteObj = {
      id: Math.random(),
      title,
      note
    }

    setNoteList([...noteList, noteObj])

    localStorage.setItem('noteList', JSON.stringify(noteList))

    setNote('')
    setTitle('')
  }

  return (
    <form>
      <input
        label="note-title"
        placeholder="Note Title"
        type="text"
        value={title}
        onChange={ (e) => setTitle(e.target.value) }
      />
      <input
        label="note-text"
        placeholder="Write your note here"
        type="text"
        value={note}
        onChange={ (e) => setNote(e.target.value) }
      />
      <button
        type="button"
        onClick={ () => submitNote() }
      >
        Send
      </button>
    </form>
  )
}

-------

components/StickNotes.jsx

export default function StickNotes() {
  const list = JSON.parse(localStorage.getItem('noteList'))

  return (
    <div>
      { list && (list
        .map(note => (
          <div key={ note.id }>
            <h1>{ note.title }</h1>
            <div>{ note.note }</div>
          </div>
        )))
      }
    </div>
  )
}
demonstration
Top answer
1 of 5
14
Also when appending to an array using set state, you need to use the callback to get the current value: setNoteList((existingNotes) => […existingNotes, noteObj])
2 of 5
5
There's a lot of bad info in this thread. First of all, your error was caused by the fact that you're passing in noteList to .setItem which refers to the original value stored in state, not the updated one. This is not because setState is asynchronous (it is) but simply because the value returned by it will only be updated in the next render, which will also cause your function to be recreated, etc. You're capturing the old value in scope of your update function. A comment mentioned that your item will not be updated from localStorage. This is false. The StickNotes component will re-render every time the parent component rendered, regardless of whether you passed in any props to it. This is a common misconception about React most people have and it may be due to the name, where one might assume it's a reactive framework, when in reality it isn't. The LocalStorage web API is also synchronous, which means your update will have applied to localStorage by the time this child runs its render cycle. To circumvent this behaviour, you can use the memo function to wrap this component, and you'll quickly notice that your notes will stop updating altogether. Should you use localStorage at all? I'd say no. If you must use it to store previous entries, you should try to avoid accessing it in the render cycle. This is because as I've mentioned it's a synchronous API that potentially reads from disc, which means it can be incredibly slow and having it in a hot path such as a render function is going to cause a lot of lag as your application grows. Another issue with how you use localStorage is that it's out of sync with your UI. When you refresh you'll see the notes from the final update before refresh but as soon as you add one, it will show only that new one. If you want to bind data to localStorage your approach should be to: Create a custom hook with a meaningful name so that you understand the abstraction later on. Access the localStorage only once when your hook first runs. Save the current value in state as the initial value of that state. On update, you should use setState to propagate changes to the rest of the UI To store the latest value in localStorage you should spawn a new task (setTimeout or push it to the next animation frame by using requestAnimationFrame) so that it doesn't block your render and call .setItem inside a callback to that. Something tells me you're a beginner or are new to React and so what I'd recommend is not to reach for localStorage at all until you have a firm grasp of other concepts discussed in this thread. It doesn't immediately play well with React, therefore I'd recommend adding it as an enhancement later on.
🌐
YouTube
youtube.com › watch
How to Update Objects and Arrays in UseState in React - YouTube
How to clone objects and arrays in ReactJS to update the state correctly and rerender the screen.⭐ Get my full-stack Next.js with Express & TypeScript course...
Published   May 30, 2023
🌐
React
react.dev › reference › react › useState
useState – React
If your updater function is pure (as it should be), this should not affect the behavior. The result from one of the calls will be ignored. Call useState at the top level of your component to declare one or more state variables. ... The convention is to name state variables like [something, setSomething] using array ...
🌐
freeCodeCamp
forum.freecodecamp.org › javascript
React useState not updating the variable - JavaScript
April 14, 2023 - Problem There is no syntax errors. Function parseLocalStorage returns a object White using setStorage(parseLocalStorage()) at useState Storage does’nt get modified setStorage used in other functions (like { const [hasParsed , setH...
🌐
Stack Overflow
stackoverflow.com › questions › 72953118 › react-usestate-hook-value-not-updating-for-dictionary-and-array › 72963488
reactjs - React:- useState Hook value not updating for dictionary and array - Stack Overflow
My suspicion is a stale callback function. saveresponse will only be updated in the next render and any function that uses saveresponse needs to depend on it so that it gets recreated. Otherwise those functions will refer to an older value. ... Yes, saveresponse will still refer to the old value after calling setSaveResponse(). See The useState set method is not reflecting a change immediately.
🌐
GitHub
github.com › facebook › react › issues › 15090
react hooks array not updating · Issue #15090 · facebook/react
March 12, 2019 - react hooks array not updating#15090 · Copy link · yahengqi · opened · on Mar 12, 2019 · Issue body actions · const mappingDashboardeColor: any = []; const [mappingPicker, setMappingPicker] = useState(mappingDashboardeColor); function handleColorPickerValueChange(index: number, colorPickerValue: any) { _.map(mappingPicker, (n, k) => { if (parseInt(k) === index) { mappingPicker[k].colorPicker = colorPickerValue.label; } }) setMappingPicker(mappingPicker); } Write like this without updating ·
Author   yahengqi