Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array is not being updated in useState hook?

I have an array of objects like this

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

I want to use this array to create chart so I need to create two arrays from names and ages. Then I want to pass it down as props to another component

So far I came up with this in my parent component

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

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

The problem here is that newNames is being created but when I set it with hook setNames, names is not updated, it is still empty. I'm not sure what am I doing wrong.
All the help will be much appreciated.

like image 748
Person Avatar asked Dec 03 '22 10:12

Person


2 Answers

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);
}, []);
like image 146
Joss Classey Avatar answered Jan 19 '23 00:01

Joss Classey


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);
like image 30
Chris Ngo Avatar answered Jan 18 '23 22:01

Chris Ngo