Laurence Fass
Laurence Fass

Reputation: 1942

Correct way to add items to an array stored in React state with useState hook?

I need to concat items to an array stored in React component state. I have an example on stackblitz and I cant understand why the array is not growing as i add elements using spread operator to concatenate to the existing array. Any help appreciated

link: https://stackblitz.com/edit/react-usestate-example-ebzt6g?file=index.js

import React from 'react';
import { useState, useEffect } from 'react';
import { render } from 'react-dom';
import './style.css';

const App = () => {

    const [name, setName] = useState('React');
    const [coords, setCoords] = useState([]);

    const success = (position) => {
        // setCoords(coords.concat(position.coords))
        setCoords([
            ...coords,
            position.coords
        ])
        console.log("success! position=", position);
    }

    useEffect(() => {
        console.log("useEffect -> coords =", coords);
    });

    useEffect(() => {
        setInterval(() => {
            success({coords : {latitude: Math.random()*51, longitude: Math.random()*2.6}});
        }, 5000);
    }, []);

    return (
    <p>example to demonstrate growing an array stored with React usestate hook</p>
    )
}

render(<App />, document.getElementById('root'));


Upvotes: 2

Views: 6454

Answers (3)

Laurence Fass
Laurence Fass

Reputation: 1942

Thanks Nicholas Tower for the correct answer.

const success = (position) => {
  setCoords(prevCoords => {
    return [
      ...prevCoords,
      position.coords
    ]
  })
}

fixes the issue.

Upvotes: 2

Salah Eddine Makdour
Salah Eddine Makdour

Reputation: 1042

Well, I had that problem and thanks to someone who helped me figure out how to solve that problem, first you have to clone the coords, and then u add the new coords to the clone and then you set the new clone coords to the state like this:

 const success = (position) => {
  let clonedCoords = [...coords];
  setCoords([...clonesCoords, position.coords]);
  console.log("success! position=", position);
};

this should work, if it doesn't you can reply so I can help u more

Upvotes: -1

Nicholas Tower
Nicholas Tower

Reputation: 85012

useEffect(() => {
  setInterval(() => {
    success({coords : {latitude: Math.random()*51, longitude: Math.random()*2.6}});
  }, 5000);
}, []);

The empty array as the second parameter tells react to create this effect only once, and never update it. When it's created, it has a reference to the success function in its closure, and that success function in turn has a reference to coords. Since this is all from the first render, coords is an empty array.

So every time you call success, you're adding the new coords to that empty array and calling setCoords. The array never grows, because your starting point is always the empty array. And you'll never see the new arrays because those only exist on later renders.

The simplest fix for this is to use the function version of setCoords. React will call the function and pass in the latest value of coords

const success = (position) => {
  setCoords(prevCoords => {
    return [
      ...prevCoords,
      position.coords
    ]
  })
}

Upvotes: 7

Related Questions