Reactjs hook useState weird behaviour

Viewed 103

I'm trying to display a list of Item from an API call to a list of components.

Here's my code:

function Content({...props}) {
  const [list, setList] = useState([])
  const [loading, setLoading] = useState(true)
  const [components, setComponents] = useState([])

  useEffect(() => {
    if (!loading) {
      return;
    }
    API.getInfo((data) => {
      setLoading(false)
      setComponents([])
      setList(data)
      console.log(data)
    })

  })

  useEffect(() => {
    if (components.length > 0) {
      return;
    }
    let tmp = [...components];
    for (const elem in list)  {
      const info = list[elem]
      API.getUserById(info.userid, (data) => {
        tmp.push(<InfoItem id={info._id} key={info._id} info={info} module={info.module} since="N/A" user={data.initial ? data.initial : `${data.firstname} ${data.lastname}`} {...props}/>)
        setComponents(tmp)
        console.log(tmp)
      })
    }
  }, [list])

    console.log(components)

    return(
        <div className="container-fluid">
            <div className="row">
                <CardHeader title="My tittle"/>
                <div className ="col-lg-12">
                    {loading ?
                      <Card content={"Loading..."}/>
                    :
                      <Card content={
                        <div style={{height: "62vh", overflow: "hidden"}}>
                            <div className="list-group h-100" style={{overflowY: "scroll"}}>
                              {components ? components : <p>Nothing</p>}
                            </div>
                        </div>
                    }/>
                    }
                </div>
            </div>
        </div>
    )
}

As you can see I use one useEffect to handle the result from the API and another one to update the components list. But when I display Content, it's always missing one or many item from the list, even when the list have only 2 elements. And when I display tmp, it's contain all the components as well as when I display the components list. I don't know why but it seems that the update of setComponents doesn't affect the return.

If I try to add some fake elements and fast reload, all the component are poping, I don't know how to force update the list component.

If someone know where that missing elements can came from it will be great. thank you.

3 Answers

I think you need to wait for the async task to finish. Try to fit an await or a .then in the API.getUserById. Your data probably has not yet been retrieved by the time the setComponents(tmp) is executed.

The error is because the tmp array stay the same, even when new item are push so the setComponents doesn't render because it's still the same array, here's what I've done to fix that:

useEffect(() => {
    if (!loading) {
      return;
    }
    API.getInfo((data) => {
      setLoading(false)
      let all = []
      for (const elem in data)  {
        const info = data[elem]
        API.getUserById(info.patientid, (data) => {
          let tmp = [...all]
          tmp.push(<InfoItem id={info._id} key={info._id} info={info} module={info.module} since="N/A" patient={data.initial ? data.initial : `${data.firstname} ${data.lastname}`} {...props}/>)
          all.push(tmp[tmp.length - 1])
          setComponents(tmp)
          console.log(tmp)
        })
      }
      
    })

  })
useEffect(() => {
    if (!loading) {
      return;
    }
    API.getInfo((data) => {
      setLoading(false)
      setComponents([])
      setList(data)
      console.log(data)
    })
  },[]);
Related