Home > Software engineering >  ReactJS array from usestate is still empty after setTimeout
ReactJS array from usestate is still empty after setTimeout

Time:10-24

please I'm solving one problem (just learning purposes). I'm using useState() hook and then, after some timeout I want add next items into array from remote fetch.

My code snippet look like:

const [tasks, setTasks] = useState([]);
const url = 'https://pokeapi.co/api/v2/pokemon?offset=0&limit=5';

// asnynchronous call. Yes, it's possible to use axios as well
const fetchData = async () => {
  try {
    let tasksArray = [];
    await fetch(url)
      .then((response) => response.json())
      .then((data) => {
        data.results.map((task, index) => {
          // first letter uppercase
          const taskName = task.name.charAt(0).toUpperCase()   task.name.slice(1);
          tasksArray.push({ id: index, name: taskName });
        });
      });

    console.log('Added tasks:'   tasks.length);
    setTasks(_.isEmpty(tasks) ? tasksArray : [...tasks, tasksArray]);
  } catch (error) {
    console.log('error', error);
  }
};

useEffect(() => {
  // Add additional example tasks from api after 5 seconds with
  // fetch fetchData promise
  setTimeout(fetchData, 5000);
}, []);

Code works fine with useEffect() hook. But in async function my array is empty when I add some tasks within five seconds and it will be replaced by fetched data and one empty

I added Butter and Milk within 5 seconds to my Shopping list

enter image description here

But after timeout my tasks array will be replaced by remote fetched data.

enter image description here

And as you can see, tasks array lenght is 0 (like console.log() says)

Please, can you exmplain me, why my tasks array is empty if there exists 2 items before 5 seconds.

Of course, I'm adding my tasks to the list normally after hit Enter and handleSubmit

const handleSubmit = (e) => {
  e.preventDefault();
  //creating new task
  setTasks([
    {
      id: [...tasks].length === 0 ? 0 : Math.max(...tasks.map((task) => task.id))   1,
      name: newTask,
       isFinished: false,
     },
     ...tasks,
  ]);
  setNewTask('');
}

Thanks for help or explain. It the problem that useEffect is called after rendering? Of this causing async behavior?

CodePudding user response:

I could not understand your code fully correctly but my guess is
the fetchData function you have declared might refer to the tasks at the time of declaration.
so every time you call fetchData you might not see the changed tasks state...
if this is the case try using useCallback with the dependency tasks...
what useCallback does is it stores the function in memory and if smth in dependency changes the function's logic changes to dependencies you declared.
If you have used eslint, calling a function inside useEffect will give you error similar to below

The ‘fetchOrg’ function makes the dependencies of useEffect Hook (at line 6) change on every render. Move it inside the useEffect callback. Alternatively, wrap the ‘fetchOrg’ definition into its own useCallback() Hook

CodePudding user response:

Your code is confusing. You can place everything inside an useEffect, and I believe the thing you are trying to achieve is a long poll query. (for that you use setInterval() and you have to clear the function in useEffect

my solution for you:

const [tasks, setTasks] = useState([]);
const url = 'https://pokeapi.co/api/v2/pokemon?offset=0&limit=5';
const request = {method: GET, Headers: {"Content-type":"application/json"}}

useEffect(() => {

   function fetchData(){
      fetch(url, request).then(res => res.json().then(data => {
      data.results.map((task, index) => {
      const taskName = task.name.charAt(0).toUpperCase()   task.name.slice(1);
      setTasks((prevState) => ([...prevState,{ id: index, name: taskName }]));
        });
})
   }

   const interval = setInterval(() => {
          fetchData();
         }, 5000)
   return () => clearInterval(interval)
}, []);

please do not forget two things:

  1. This approach is only good when you have 5 to 10 simultaneous clients connected since it is not performance effective on the backend, I would recommend instead an alternative based on realtime communication (with the listening for new events.

  2. please do not forget to specify the {method, headers, body) in the fetch function

  • Related