'Array contents can't be retrieved (React JS) [duplicate]

I am getting the content in an array and I can see the contents of the array on console.log.

However, the length is 0 and the contents cannot be retrieved with a map. Why is this?

enter image description here

const contents = [] as any[]

useEffect(() => {
  for(const key of Object.keys(fruits)) {
    contents[key as any] = {
      ...fruits[key as any],
      ...vegetables[key as any],
    }
  }
}, [fruits, vegetables, contents])


console.log('contents', contents)
console.log('contents length', contents.length)


Solution 1:[1]

You can try to have contents in the state instead of exposing it as a variable.

contents will be updated accordingly and correctly whenever fruits or vegetables get updated.

const [contents, setContents] = useState([])

useEffect(() => {
  const updatedContents = [...contents]
  for(const key of Object.keys(fruits)) {
    updatedContents[key as any] = {
      ...fruits[key as any],
      ...vegetables[key as any],
    }
  }
  setState(updatedContents)
}, [fruits, vegetables])


console.log('contents', contents)
console.log('contents length', contents.length)

Solution 2:[2]

useEffect executes after each render.

So the order of operations in your example is:

  1. useEffect scheduled to run after render
  2. Rest of the code is executed (including your console.logs, hence you seeing 0 in the console.log)
  3. useEffect executes, updating contents with the data.

In Chrome's dev tools, javascript arrays/objects are displayed by their reference, evaluated lazily. What this means is that if something is added to the array/object later, and if you expand it later than when it was printed, you'll see the new data.

That answers the why.

As a solution, here's what you can do instead:

const [contents, setContents] = useState([]);

useEffect(() => {
  const newContents = [];
  for(const key of Object.keys(fruits)) {
    newContents.push({
      ...fruits[key as any],
      ...vegetables[key as any],
    });
  setContents(newContents);
  }
}, [fruits, vegetables, contents])

console.log('contents length', contents.length);

You'll see the appropriate console log once the state updates.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2