1

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)
k10a
  • 947
  • 2
  • 11
  • 30
  • 1
    there's the chance that the way you are filling the array use an async operation and the length property isn't synced yet with the actual number of values because that operation didn't finish yet before console.log outputs the length. There are some hints here: https://stackoverflow.com/questions/42260524/array-length-is-zero-but-the-array-has-elements-in-it – Diego D May 09 '22 at 06:07
  • 1
    You should probably use a state here: https://reactjs.org/docs/hooks-state.html –  May 09 '22 at 06:11

2 Answers2

0

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)
Nick Vu
  • 14,512
  • 4
  • 21
  • 31
-1

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.

Azarro
  • 1,776
  • 1
  • 4
  • 11
  • Not sure why 3 answers got instantly downvoted on this thread as soon as they were posted, but at least for mine this is easily confirmable with OP's example as well as any other rudimentary react example + knowledge of useEffect https://reactjs.org/docs/hooks-effect.html – Azarro May 09 '22 at 06:12
  • 1
    Because there's no answer you could post that would turn any of this into a useful addition to stackoverflow. OP is using React wrong, and Chrome evaluates lazily. This question is probably a result of the two. Posting an answer here contributes absolutely nothing and will not be useful for future readers either (which is what it should be, we are not here to solve people's beginner problems) –  May 09 '22 at 06:13