React UseEffect render infinite loop - javascript

I seem to have an infinite loop in my code but I can't seem to see where, I usually see this if I am setting the state of something that is a dependency of useEffect but here the two variables (values / items) are completely seperate.
App.js
import React from 'react';
import './style.css';
import MyComponent from './MyComponent.js';
export default function App() {
return (
<div>
<MyComponent />
</div>
);
}
MyComponent.js
import React, { useEffect, useState } from 'react';
const MyComponent = ({ values = [] }) => {
console.log('MyComponent Reloaded');
const [items, setItems] = useState();
useEffect(() => {
const loadItems = () => {
//setItems([]); //why does this cause infinite render loop?
};
loadItems();
}, [values]);
return <></>;
};
export default MyComponent;
Why does this cause a render loop?
I have an example build Here (Uncomment the commented line to begin render loop)

You need to introduce the default values of the props the right way:
import React, { useEffect, useState } from 'react';
const MyComponent = ({ values }) => {
console.log('MyComponent Reloaded');
const [items, setItems] = useState();
useEffect(() => {
console.log(values)
const loadItems = () => {
setItems([]);
};
loadItems();
}, [values]);
return <></>;
};
MyComponent.defaultProps = {
values: []
}
export default MyComponent;

There are two possible bugs in the code:
According to your code, values variable is created every time, when checked with the previous values variable it is not same. So it causes infinite loop.
To fix this use default props.
const MyComponent = ({ values }) => {
...
MyComponent.defaultProps = {
values: []
}
As in your question,this line causes you the infinite loop
//setItems([]);
To overcome this
Add items dependency along with values, so that both values are watched before re-rendering.
useEffect(() => {
console.log(values)
const loadItems = () => {
setItems([]);
};
loadItems();
}, [values,items]);

Related

How to fetch API as soon as page is loaded in React?

Whenever I visit a page it should automatically fetch the API
import React from 'react'
const Component = () => {
fetch("api url").then((res) => console.log(res))
return (
<div>comp</div>
)
}
export default Component
It is very simple using react hook use effect please learn basics of useffect hook on react docs or any youtube tutorial and as for the answer
import React, { useEffect } from 'react'
const comp = () => {
useEffect(() => {
fetch("api url").then((res)=>console.log(res))
}, [])
return (
<div>comp</div>
)
}
export default comp
here empty dependency means every time page loads only once
use the useEffect for this.
The useEffect method will execute the passed callback on the mount of the component and on every time one of the dependency array parameters is changed. therefore:
const Comp = () => {
useEffect(() => {
fetch("api url").then((res)=>console.log(res))
}, []);
return (
<div>comp</div>
)
}
Will make the callback to fire only once (because the empty dependency array) on the component mount.
You should use the useEffect Hook in your principal component like app.js
import React, {useEffect} from 'react'
useEffect(() => {
fetch("api url").then((res)=>console.log(res))
}, []);
Be careful, this manipulation can consume a lot of resources (a lot of data to fetch etc.)
Thery
import React, { useState, useEffect } from 'react'
const Comp = () => {
const [ data, setData ] = useState([]);
const getData = async () => {
const res = await fetch("api url");
const data = await res.json();
setData(data)
}
useEffect(()=>{ getData() },[]);
return (
<>
<div>comp</div>
// dispaly your data here from data state
</>
)
}
export default Comp;
Fetch and use data with useState
const initialValue = {};
const comp = () => {
const [data, setData] = useState(initialValue);
useEffect(() => {
let ignore = false;
const fetchData = async () => {
const res = fetch("api url");
if (ignore) { return; }
setData(res.json())
return () => {
ignore = true;
}
}
, [])
return (
<div>comp {data.prop}</div>
)
}
More on working with state
More about useEffect life cycle
Hope it helps
You don't need to use the API function like this, it will be called continuously, you need to use useEffect hook, when your component reloads useEffect will be called, and you can learn about the useEffect dependency here,
import React, { useEffect, useState } from 'react'
const comp = () => {
const [data, setData] = useState([]);
useEffect(() => {
fetch("api url").then((res)=> {
console.log(res)
setData(res)
} )
}, [])
return (
// use data state to show the data here
<div>comp</div>
)
}
export default comp;

The object passed as the value prop to the Context provider changes every render. How to Fix it with useMemo?

I have the following problem,
The object passed as the value prop to the Context provider (at line 20) changes every render. To fix this consider wrapping it in a useMemo hook.
I don't know hot to use useMemo in this case. So how do I fix it?
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import Context from './Context';
function Provider({ children }) {
const [data, setData] = useState([]);
useEffect(() => {
const getDataAPI = async () => {
const response = await fetch('https://swapi-trybe.herokuapp.com/api/planets/');
const dataAPI = await response.json();
const alteredData = dataAPI.results.map(({ residents, ...params }) => params);
const result = [...alteredData];
setData(result.sort((a, b) => a.name.localeCompare(b.name)));
};
getDataAPI();
}, []);
return (
<Context.Provider value={ { data } }>
{ data[0] && children }
</Context.Provider>
);
}
Provider.propTypes = {
children: PropTypes.node.isRequired,
};
export default Provider;
Look at what you are passing:
value={ { data } }
It is an object {data : data}. This is defined everytime.
You better pass value={data} and change your useContext hooks accordingly in children

Getting infinite loop and not able to fetch data in React

This the code that is responsible for getting Data and updating the list:
import {createSlice} from "#reduxjs/toolkit";
import axios from "axios";
const initialState = {
log: []
}
let page = 1;
const cartSlice = createSlice({
name: 'cart',
initialState: initialState,
reducers: {
fetching: (state, payload) => {
state.log = axios.get(`http://localhost:5000/fetch/?${page = payload.payload}`)
// console.log(state)
}
}
})
export const {fetching} = cartSlice.actions;
export default cartSlice.reducer;
And this is the Home Page:
import React, {useEffect} from "react";
import {useDispatch, useSelector} from "react-redux";
import {fetching} from "../features/cardSlice";
import Cards from "./Cards";
export default function HomePage(){
const dispatch = useDispatch();
const {itemsList} = useSelector((store) => store.card)
console.log(itemsList)
const {pageNumber} = useSelector((store) => store.page);
useEffect(()=>{
dispatch(fetching(1)); **// This is where i call dispatch to update the state and get the data**
})
function cardMapper(items) {
return(
<Cards
name = {items.name}
key = {items.id}
cuisine={items.cuisine}
address={items.address}
/>
)
}
return(
<div>
{/*{itemsList.map(cardMapper)}*/}
</div>
)
}
When i run this on localhost i am not able to get data, the console.log(itemList) is showing undefined and also the dispatch(fetching(1)) is called infinite times.
Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
I am not able to understand why i'm getting an infinite loop and also why am i not getting the data.
I modified your code. To prevent infinite loop in using useEffect you should put [] as dependency
And for the useSelector,the variable should not enclosed by {}.
see the modified code below.
As I see, it should be cart not card
Try this code instead
export default function HomePage(){
const dispatch = useDispatch();
const itemsList = useSelector((store) => store.cart)
console.log(itemsList)
const {pageNumber} = useSelector((store) => store.page);
useEffect(()=>{
dispatch(fetching(1)); **// This is where i call dispatch to update the state and get the data**
},[])
function cardMapper(items) {
return(
<Cards
name = {items.name}
key = {items.id}
cuisine={items.cuisine}
address={items.address}
/>
)
}
return(
<div>
{/*{itemsList.map(cardMapper)}*/}
</div>
)
}
also in reducer
add a return
fetching: async (state, payload) => {
const newState = await axios.get(`http://localhost:5000/fetch/?${page = payload.payload}`)
return newState;
}
// this should end the infinite loop
useEffect(()=>{
dispatch(fetching(1)); // This is where i call dispatch to update the state and get the data
}, [ ])
You need added dependencies as an empty array. To remove the yellow
squiggly line you need to add dispatch inside that dependencies array.
however, react to ensure that dispatch dependencies never change and
that you are not responsible for re-render your component.
useEffect(()=>{
dispatch(fetching(1))
},[dispatch])

react useContext setState is not a function

I'm working on a react app and I need to keep an array of names in my global state like on this file:
import React from "react";
import { useState } from "react";
const initialState = {
nameList: [],
test: 'hello'
}
export const Context = React.createContext()
const Data = ({ children }) => {
const [state, setState] = useState(initialState)
return(
<Context.Provider value={[state, setState]}>
{children}
</Context.Provider>
)
}
export default Data
However, when I try to set "nameList" to a new value in this other file:
const [state, setState] = useContext(Context);
const [currName, setCurrName] = useState('');
const handleAddName = () => {
setState.nameList(prevState => [...prevState, currName])
}
I get a "setState.nameList is not a funtion" error and I can't find nor understand the reason why, any help would be much appreciated
You're updating the state incorrectly, here's how to do it in your case:
const handleAddName = () => {
setState(prevState => ({
...prevState,
nameList: [...prevState.nameList, currName]
}))
}
This will make sure that the state is updated immutably.
setState.nameList is wrong because setState is a function but not an object that will magically have the keys of your state.

Too many re-renders. React limits the number of renders to prevent an infinite loop when passing states?

I am trying to access state from one component to another
FetchCompo.js
// import React from "react";
import React, { useState, useEffect } from "react";
//more imports
const FetchUserItems= () => {
//some state
const [userFirstName, setUserFirstName] = useState("");
const [userItem, setUserItem] = useState([]);
let userName = //somecode
setUserFirstName(userName);
let userItemsData= userData.MyArray.items;
if (userItemsData.length === 0) {
const emptyItems = [
{
//obj data
},
];
setUserItem(emptyItems );
} else {
//someData
setUserItem(userItemsData);
}
return { userFirstName, userItem};
};
export default FetchCompo;
I wanted to use userFirstName, userItem in the another Test.js component.
// import React from "react";
import React, { useState, useEffect } from "react";
import FetchCompofrom "../myFunctions/FetchCompo";
//more imports
const Test = () => {
//Wanted to use userFirstName in Test.js component
const { userFirstName, userItem } = FetchCompofrom();
return (
<div>{userFirstName}</div>
)
}
when I am trying to get the userFirstName, userItem in the Test.js component then getting error of Too many renders
looking for a solution how i can access these state userFirstName, userItem form one component to another.
You're actually importing the React Component not the FetchUserItems helper function...
import FetchCompofrom "../myFunctions/FetchCompo";
But you could do something like...
const [userFirstName, setUserFirstName] = useState('');
const [userItem, setUserItem] = useState([]);
const FetchUserItems = () => {
/**
* Make it plain helper function for fetching userItems
* Do-not set-state here...
*/
return { userFirstName, userItem };
};
export const FetchUserItems;
/** In your component ... say in useEffect */
const result = FetchUserItems();
/** setState here in case of result */
In Test.js
import { FetchUserItems } "../myFunctions/FetchCompo";
Your are using setUserFirstName(userName) in FechUserItems, outside useEffect or normal function, this will provoque the component to re-render indefinitely because setting the states provoques re-rendering.
I would suggest to make FetchUserItems a normal function, because you are not rendering anything in it. You could use only Test comp for it.
The Test comp would be something like this:
// import React from "react";
import React, { useState, useEffect } from "react";
import FetchCompofrom "../myFunctions/FetchCompo";
//more imports
const Test = () => {
const [userFirstName, setUserFirstName] = useState("");
const [userItem, setUserItem] = useState([]);
useEffect(() => fetchUserFirstName, [])
const fetchUserFirstName = () => {
// your code here and
// setUserFirstName in the end
}
//Wanted to use userFirstName in Test.js component
const { userFirstName, userItem } = FetchCompofrom();
return (
<div>{userFirstName}</div>
)
}

Categories