2

I initialised state like this:

const [titles, setTitles] = useState([])

I'm appending another array of items data.titles to the array titles in a loop like this:

setTitles(titles => [...titles, data.titles])

and I have also tried this:

setTitles([...titles, data.titles])

but both don't seem to work, because when I'm printing titles in the console after the loop is done, an empty array is displayed. What am I missing here?

isherwood
  • 58,414
  • 16
  • 114
  • 157
DSteman
  • 1,388
  • 2
  • 12
  • 25
  • Well it depends on what is actually in `data`... Is it called `titles` or `title`? Is it an array itself or just another entry? You could be looking for [`concat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat), but I can't be sure without more details. – Brian Thompson Jun 03 '21 at 18:21
  • Another important detail is what exactly "both don't seem to work" means. Does it throw an error? Is the data just not in the format you expect? If so, what error or format are you getting? – Brian Thompson Jun 03 '21 at 18:22
  • After the edit: Looks like you could just do `setTitles(titles.concat(data.titles))`. However "after the loop is done" signals that there may be other issues here, as setting state in a loop will overwrite any previous state updates, and because you cannot console log state to see the new value until the next render. – Brian Thompson Jun 03 '21 at 18:23
  • @ShubhamKhatri I'm console logging 'titles' in a promise on the loop though, so that should mitigate the async aspect. – DSteman Jun 03 '21 at 18:29
  • 1
    @ShubhamKhatri, actually you were right. In the React dev tool I can see that the state is updating and values are being appended. The way how you suggested to merge the prev state and new data worked for me as well. – DSteman Jun 03 '21 at 18:44
  • 1
    The problem in functional components is also about closures and not just asynchronous behaviour. I wrote a short article [here](https://betterprogramming.pub/why-dont-react-state-updates-reflect-immediately-9041c4377385) if you are interested in reading. – Shubham Khatri Jun 03 '21 at 18:44
  • Cool, thanks. Appreciate the effort you are putting in the community. – DSteman Jun 03 '21 at 18:46

1 Answers1

1

You shouldn't use the previous state directly while setting the new state as state changes are asynchronous. Also, Since your second param (data.titles), is an array, you should iterate through it too using the spread operator.

setTitles(prevState => [...prevState, ...data.titles])

A simpler way to do this would be using the array concat operator,

setTitles((prevState) => prevState.concat(data.titles));
NeERAJ TK
  • 2,447
  • 1
  • 11
  • 25
  • Actually this question had already been answered. It also suggested the right way to merge new data with the prev state. This is the example that was given in that other post: setMovies(prevMovies => ([...prevMovies, ...result])); – DSteman Jun 03 '21 at 18:45
  • @DSteman Oh okay. Didn't notice the comment – NeERAJ TK Jun 04 '21 at 03:51