0

i have made an API in Django for fetching the TODO list tasks in my React JS Application but when i use allTasks of useState in dependency array of useEffect Hook then it starts sending endless get requests. I can't understand how is this happening also if i leave the dependency array empty then it works fine but then i have to refresh the page manually if some data changes in background.

Here below is the code of Tasklist.js Component

import React,{useState, useEffect} from 'react'
import axios from 'axios'   
import Task from './Task'

function TaskList() { 
    async function getAllTasks(url){
        let resp = await axios.get(url);  
        let all_tasks= await resp.data   
        return all_tasks;
    }
    const [allTasks, setAllTasks]= useState([])
    useEffect(()=>{
        async function appendTasks(){
            let alltasks= await getAllTasks('http://127.0.0.1:8000/api/retrieve')
            setAllTasks(alltasks)
        }
        appendTasks()
    },[allTasks])
    return (
        <div className="TaskList">
            {
                allTasks.map(function(task){ 
                    return( 
                        <Task key={task.id} task={task} /> 
                    )
                })
            }
        </div>
    )
}

export default TaskList


  • 1
    The useeffect hook will run every time one of the marked dependecies will change. So in that case remove the allTasks from the dependency array – Oliver Ilmjärv Aug 13 '21 at 13:24
  • Add `some data changes in background` in the dependencies instead `allTasks` – Viet Aug 13 '21 at 13:24
  • Does this answer your question? [Infinite loop in useEffect](https://stackoverflow.com/questions/53070970/infinite-loop-in-useeffect) – im_baby Aug 13 '21 at 13:28
  • @im_baby no it doesn't –  Aug 13 '21 at 13:46
  • @OliverIlmjärv but if i remove allTasks from dependency array then it will not update automatically.. if the tasks changes –  Aug 13 '21 at 13:48
  • You are causing the infinite loop because you are saying "whenever this data changes: update this data". You can't both set state in the useEffect and trigger the useEffect when state changes. – im_baby Aug 14 '21 at 03:23

3 Answers3

1

You got an infinite loop because you're modifying your useEffect dependency in the useEffect itself.

Remove your dependency to solve your problem :

useEffect(()=>{
    async function appendTasks(){
        let alltasks= await getAllTasks('http://127.0.0.1:8000/api/retrieve')
        setAllTasks(alltasks)
    }
    appendTasks()
},[])

Remove the dependency. If you want to refresh your data you can, for example, set an interval to call your backend every interval of time.

useEffect(()=>{
    async function appendTasks(){
        let alltasks= await getAllTasks('http://127.0.0.1:8000/api/retrieve')
        setAllTasks(alltasks)
    }
    const interval = setInterval(() => appendTasks(), 30000) // 30000ms = 30 secondes
    return () => clearInterval(interval);
},[])

Here you will refresh your tasks every 30 secondes.

There is of course other options depending of your needs.

GregMit
  • 490
  • 2
  • 11
0

1st of all - useEffect is run everytime its dependencies change, in this case its because you specify allTasks as dependency and in useEffect itself you call setAllTasks which changes allTasks so useEffect is called again, so setAllTasks is called again, so allTasks is changed and useEffect called again, and so on... I hope you understand now why you are getting infinite number of calls. Now solution - you don't really need allTasks dependency in this case - setAllTasks is enough - why ? because nowhere in useEffect you use allTasks variable.

Solution:

useEffect(()=>{
        async function appendTasks(){
            let alltasks= await getAllTasks('http://127.0.0.1:8000/api/retrieve')
            setAllTasks(alltasks)
        }
        appendTasks()
    },[setAllTasks]) // notice the change here

2nd - not related to question, but you should include all dependencies in useEffect dependency array - in this case function getAllTasks should be included as well - but this would make infinite number of calls again - i suggest you to read something about useMemo and useCallback, but easiest solution for you should be something like this

useEffect(()=> {
    async function getAllTasks(url){
      let resp = await axios.get(url);  
      let all_tasks= await resp.data   
      return all_tasks;
    }
    let response = await getAllTasks('http://127.0.0.1:8000/api/retrieve')
    setAllTasks(response)    
}, [setAllTasks]) 
MrHolal
  • 329
  • 3
  • 5
0

The thing you need to replace in your code is useEffect dependency array
"allTasks" with "addTodos"
eg: const [addTodos, setAddTodos]= useState([])

Note: look at the below snippet to get an idea

import {useState } from "react";
import GetTodoList from "./GetTodoList";

export default function AddTodo() {

const [input, setInput]= useState("");
const [addTodos, setAddTodos]= useState([])

const setInputValue =(e)=>{
    setInput(e.target.value)
}

const addToLocalStorage=(e)=>{
    e.preventDefault();
    setAddTodos([...addTodos, {"task": input}])
    localStorage.setItem("todo", JSON.stringify(addTodos))
}

return (
    <div>
        <input value={input} onChange={setInputValue} type="text" />
        <button onClick={addToLocalStorage}>Add addTodos</button>
        <GetTodoList addTodos={addTodos}/>
    </div>
)

}

import { useEffect, useState } from "react";

function GetTodoList({addTodos}) {

const [todoList, setTodoList] = useState([])

const getTodo=()=>{
    const result = localStorage.getItem("todo");
    setTodoList(JSON.parse(result))
    //setTotos(JSON.parse(result))
}

useEffect(() => {
    getTodo();
}, [addTodos]) //addTodos as dependency, when the setAddTodos updates then useEffect recalls getTodo() with out hard refresh 
return (
    <div>
      {(todoList)?
        todoList.map(todo=><p>{todo.task}</p>) : ""}
    </div>
)

}

export default GetTodoList