add array to array state in react hooks gives a number

Viewed 50

i declare a Array sate and want to add some Arrays in it like :

const [number, setNumber] = useState(3);
const [TimePickerList, setTimePickerList] = useState([]);
useEffect(() => {
        
        for(var i = 0; i < number ; i++) { 
           setTimePickerList(TimePickerList.push([]))
           
        }
        console.log(TimePickerList); // it gives me an array like this : [[],[],[]]
    }, [number])

but when i get log from TimePickerList in another function it gives me a number (3). and i lost my arrays in my array state (I expected something like this : [[],[],[]] But While gives me the length of the array) I also used concat() but it gives me an empty Array and doesn't add my Arrays

5 Answers

You can push the values in an empty array. Then set it to your state

    const [number, setNumber] = useState(3);
    const [TimePickerList, setTimePickerList] = useState([]);
    useEffect(() => {
            var list = []
            for(var i = 0; i < number ; i++) { 
               list.push(i)
            }
            setTimePickerList(list)
        }, [number])
           
  1. Don't mutate the state object, it messes up change detection,
  2. and the value of the expression someArray.push(someValue) is someValue.

Instead do something like

let newList = [...TimePickerList];

for(let i = 0; i < number; i++) {
    newList = [...newList, []];
}

setTimePickerList(newList);

The state works a bit differently as compared to normal variables and shouldn't be updated directly (for reasons mentioned here https://medium.com/analytics-vidhya/why-we-should-never-update-react-state-directly-c1b794fac59b)

So, I'd recommend making three changes to your code.

  1. Create a temporary variable which contains your list inside the (and you can update it wherever and however you want), e.g. inside your useEffect before for loop const list = [];

  2. Push whatever you want to push inside the variable. So for the above code, it would be: for(let i = 0; i < number ; i++) { list.push(whatever you need to push to the array); }

  3. set the state using that variable. In your case, after the for loop, update the state as: setTimePickerList(list)

The fastest way to push an element to a state containing an array is using a callback:

const [number, setNumber] = useState(3);
const [TimePickerList, setTimePickerList] = useState([]);

useEffect(()=>{
 let list = [];
 for(let i = 0; i < number ; i++) { 
   list.push([])
 }
 setTimePickerList((currentArray) => [...currentArray,...list])
},[number])

The only thing I'm guessing is: why adding empty arrays? You know that filling up these arrays will be a nightmare and since you are using a complicated data structure maybe useState is not what you want to use?

NOTICE:

Since changes of state are not performed synchronously you cannot watch the updates to TimePickerList directly in the first useEffect. To see how it changes add a second useEffect:

useEffect(()=>{
  console.log(timePickerList);
},[timePickerList])

I've tried your code and it gives a correct output as you wish.

output

But if you want to set value based on a prev value it's better to pass a callback into setter function which receives current state value

Another point - it's better to call setter function once

const [number, setNumber] = useState(3);
const [timePickerList, setTimePickerList] = useState([]);

useEffect(() => {
  setTimePickerList((prev) => {
    const list = [...prev];
    for(let i = 0; i < number ; i += 1) { 
      list.concat([]);
    }

    return list;
  })
}, [number])
Related