useEffect dependency array causing infinite loop [duplicate] - reactjs

This question already has answers here:
Understanding the React Hooks 'exhaustive-deps' lint rule
(2 answers)
Closed 1 year ago.
I'm getting the following warning in the console:
Line 19:6: React Hook useEffect has a missing dependency: 'data'. Either include it or remove the dependency array and res.data is an empty array when I console log it.
But when I pass in data to the dependency array, I do get the correct API response in the console, but I get an infinite loop.
From what I've read, this is one of the most common traps to fall into when using useEffect, but I still have a hard time wrapping my head around how to resolve this or finding an answer I can truly understand.
Any help would be appreciated in what is currently wrong with my code, and how to resolve.
import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
const apiKey = process.env.REACT_APP_NASA_KEY;
const NasaPhoto = () => {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const res = await axios(
`https://api.nasa.gov/planetary/apod?api_key=${apiKey}`
);
setData(res.data);
console.log(data);
};
fetchData();
}, []);
return (
<div>
<Link to='/'>Return Home</Link>
<h1>Nasa Data</h1>
</div>
);
};
export default NasaPhoto;

write your fetch data method outside the useEffect and call it in the useEffect then pass it as a dependency
your code should now be something like this
`
const fetchData = async () => {
const res = await axios(
https://api.nasa.gov/planetary/apod?api_key=${apiKey}
);
setData(res.data);
console.log(data);
};
useEffect(() => {
fetchData();
}, [fetchData]);
`

Related

Trying to use isPending in React 18 without Suspense and not getting expected results

I understand that the isPending return in React 18 is used so that lower priority state updates can happen last. I'm struggling to figure out how to make isPending work in a simple REST GET call example. I'm trying to write the code below without having to use a separate isLoading type state.
Is there anything I can do to make this happen? That is, with only isPending render a "loading..." message until the data has been retrieved?
(the results I get from the code below is I see a flash of "loading", then immediately see [] followed by my data. I want to see "loading" until my data actually loads)
import axios from "axios";
import { useEffect, useState, useTransition } from "react";
export default function Test1() {
const [data, setData] = useState([])
const [isPending, startTransition] = useTransition();
useEffect(() => {
async function fetchMyAPI() {
startTransition(async () => {
const results = await axios.get("/api/rest");
setData(results.data);
})
}
fetchMyAPI()
}, [])
if (isPending) return <div>Loading...</div>
return (
<div>{JSON.stringify(data)}</div>
);
}

Difficulties with useEffect and asyncawait

I've read several questions here regarding my current difficulty; they also told me the way I was coding it was wrong, and I changed it. However, even after changing I still can't seem to get the proper result.
I'm trying to make a small React HTTP Request app for learning purposes. According to the classes I've been following, I managed to create the json server, setup to watch for the DB.json properly, etc. Now, inside the App.js I'm trying to make the async\await call for listing the products in the console.
First, I had the following error:
"Effect callbacks are synchronous to prevent race conditions. Put the async function inside:"
I fixed it by changing my code. It was triggering a warning and I found out the classes I've been following are a bit outdate. No problem. However, even after changing it I can't view the products I create on db.json. If I go to localhost:3000/products it shows up there (which means things are working).
I believe I'm doing it the right way now, but I still can't seem to figure out why I can't view the data.
Any input is appreciated. Thanks!
ps: I'm just starting with react.
App.Js
import './App.css';
import { useState, useEffect } from "react";
const url="http:/localhost:3000/products";
function App() {
const [products, setProducts] = useState ([]);
useEffect(() => {
const fetchData = async () => {
const data = await fetch(url);
console.log("Data:" + data)
const res = await data.json();
console.log("Res:" + res)
setProducts(res);
}
fetchData();
}, []);
console.log(products);
return (
<div className="App">
<h1>LIsta de produtos</h1>
</div>
);
}
export default App;
The URL you put is missing a "/", Check if the URL is right, rest else seems to be correct, the code below should work.
import "./App.css";
import { useState, useEffect } from "react";
// URL is probably wrong, this is fixed URL
const url = "http://localhost:3000/products";
function App() {
const [products, setProducts] = useState([]);
useEffect(() => {
const fetchData = async () => {
const data = await fetch(url);
console.log("Data:" + data);
const res = await data.json();
console.log("Res:" + res);
setProducts(res);
};
fetchData();
}, []);
console.log(products);
return (
<div className="App">
<h1>LIsta de produtos</h1>
</div>
);
}
export default App;

After useEffect API call, state set by useState for json data being passed to a component as props returns empty array

I'm still a beginner in React and I'm trying to use useEffect to fetch data from an API and then useState to set the state and then pass that state as props to a child component.
But in my child component, it appears as an empty array each time when I do console.log. I understand that on the first render the state of my initial state is an empty array []. But I've been trying to combat this and send the right JSON data but can't seem to do so.
I am trying to do this as I have multiple child components that I wanna send data to.
Below is a workaround I coded up with some digging around but doesn't work either:
const api = 'url string'
const [races, setRaces] = useState([]);
const [races2, setRaces2] = useState([races]);
useEffect(() => {
fetch(api)
.then((resp) => resp.json())
.then((response) => setRaces(response));
}, []);
useEffect(() => {
if (races.length) setRaces2(races);
}, [races]);
<Child data={races2}
But this does not seem work to work either when I do console.log(props.data) in the child component.
This is how normally one would fetch data and try and send the data but in both cases, it's been the same.
const api = 'url string'
const [races, setRaces] = useState([]);
useEffect(() => {
fetch(api)
.then((resp) => resp.json())
.then((response) => setRaces(response));
}, []);
<Child data={races}
Following is a rough flow diagram explaining what I wanna do:
Thank you for your help in advance.
I made this quick example.
Here is what the code does:
Fetching the Data using UseEffect
Storing into State
Passing the State into Component as Props
Fetching the Props and Displaying the data.
Code for App.js
import "./styles.css";
import ChildComponent from "./ChildComponent";
import { useEffect, useState } from "react";
export default function App() {
const [title, setTitle] = useState(null);
// * Init on Page Load
useEffect(() => {
fetchTitle();
}, []);
const fetchTitle = async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts/1"
);
const data = await response.json();
setTitle(data.title); //Setting the response into state
};
return (
<div className="App">
<ChildComponent data={title} />
</div>
);
}
Code for ChildComponent.js
export default function ChildComponent({ data }) {
return <div>{data}</div>;
}
I created this Codesandbox. This might help.
https://codesandbox.io/s/elegant-lumiere-cg66nt
Array and object are referential data types, passing as array dependency will not re-run side effect. useEffect dependencies should be primitive data type (string, number, boolean,undefined or null).
useEffect(() => {
if (races.length) setRaces2(races);
}, [races.length])// Dependencies must be primitive data type not referencial.

React JS useEffect hook send infinite no of request if i add an dependency

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
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.
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])
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

How to load a list on page startup with user_id using useEffect

I have been truing to load a list on a page from the user_id that i get from the parent page, but I keep getting a React Hook useEffect has a missing dependency: 'newuser_id'. Either include it or remove the dependency array react-hooks/exhaustive-deps warning and then an error saying Uncaught TypeError: Cannot read property 'length' of undefined now it doesn't even read the useEffect anymore..
This is my file:
import React, {useEffect, useState} from 'react';
import { authenticationService } from '../../services/authentication.service';
export default function Kilometer({user_id}) {
const [kmListe, setKmListe] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(false);
console.log('root ' + user_id);
useEffect(() => {
console.log('useEffect' + user_id)
setIsLoading(true)
if(!user_id){
setKmListe('')
console.log('!user_id ' + kmListe)
}else{
const getData = async () => {
await authenticationService.kmliste(user_id)
.then(
km => {
console.log('test 1 '+JSON.stringify(km))
setKmListe(JSON.stringify(km))
setIsLoading(false)
}
).catch(err => {
console.log('catch ' + err)
setError(true)
setIsLoading(false)
})};
getData()
}
}, [kmListe])
const liste =
setIsLoading(true)
if(kmListe === ''){
console.log('blank '+kmListe)
return ['No list','add km to make a list'];
}else{
kmListe.map(listen =>{
console.log('map ' + listen)
return(
<div key={listen.id}>
<div>
<h1>{listen.km_start} {listen.km_slut}</h1>
<h2>{listen.start_by} {listen.slut_by}</h2>
</div>
</div>
)
})}
console.log('return '+liste)
return error ?
<h1>{error}</h1> :
isLoading ?
<h1>LOADING.......</h1> :
liste.length ? <h1>{error}</h1> : (
<div>
{liste}
</div>
);
}
I have kept the console.logs in the code so you can see from my output on the console what is run
root 2
return undefined
root 2
return undefined
EDIT
Now no matter what I do useEffect doesn't fire at all, I stuck and I don't know how to get past it.
I have tried to remove the newuser_id and I have tried making a new page but same result..
A couple of problems in this component.
useEffect must accept a function without parameters. Something like () => { /* effect*/ }. By passing newuser_id, it overshadows the variable declared earlier in the code. Therefore, remove it from the parameter and pass it in the dependency array. By the way, why are you even declaring newuser_id instead of using user_id directly?
setKmListe schedules an update. kmListe is not updated right away.
The warning mentions the hook dependencies. Not sure if that will fix all your problems, but I feel it is a good start.
The dependencies are defined as the second parameter of the useEffect hook.
Example:
useEffect(() => {
const fetchUser = async () => {
const response = await fetch(`http://something.com/users/%{userId}`);
const user = await response.json();
setUser(user);
};
if (userId) fetchUser();
}, [userId])
Notice that userId is part of the useEffect hook dependencies.
The general rule is to not lie about the dependencies. Means that if something is defined outside of your useEffect, but used inside, it should be part of the dependencies.
Read more here
Also, kmListe being a hook dependency is an infinite loop waiting to happen because you redefine the state inside the hook. Every time the state is changed, since it is part of the dependencies, the effect will run again, and again on every render.

Resources