in my react component, I am getting the following issue in my console
index.js:1 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render. at Id (http://localhost:3000/main.82c936520e7a7a921612.hot-update.js:32:27) at div at div at div at App (http://localhost:3000/static/js/main.chunk.js:249:62)
The number of time that error shows keeps increasing each new second, the same error is created again. I have no idea why because I thought my logic makes sense, how is it creating an infinite loop?
import React, { useEffect } from "react";
import ReactTooltip from "react-tooltip";
import { FaQuestionCircle, FaRedoAlt } from 'react-icons/fa';
interface Props {
setId: React.Dispatch<React.SetStateAction<{data: number, componentRef: React.RefObject<HTMLDivElement>}>>;
id : {data: number, componentRef:React.RefObject<HTMLDivElement>};
}
const Id : React.FC<Props> = ( props : Props ) => {
var idData = {...props.id};
useEffect(()=>{
props.setId(idData);
}, [idData]);
const generateNewId = () => {
idData.data = Math.floor( Math.random()*1000 ) 1;
}
useEffect(()=>{
generateNewId();
},[])
const modifyId = (sign : string) => {
sign === " " ? idData.data = idData.data 1 : idData.data = idData.data - 1 ;
}
return(
<div>
<ReactTooltip id="counter-info">
<p>A random ID is generated each time you click refresh</p>
<p className="text-bold">What is ID?</p>
<p className="text-bold">Your ID is added to the back of your username, to always ensure that you have a unique username</p>
</ReactTooltip>
<h2>Your ID: {idData.data}
<span data-tip data-for="counter-info">
<FaQuestionCircle/>
</span>
</h2>
<button className="btn rounded-circle btn-dark" onClick={()=>modifyId("-")}>-</button>
<button className="btn rounded-circle btn-dark mr-3" onClick={()=>modifyId(" ")}> </button>
<button className="btn" onClick={()=>generateNewId()}><FaRedoAlt/></button>
</div>
)
}
export default Id;
In App.tsx
const idRef = React.useRef<HTMLDivElement>(null);
const [ id, setId ] = useState<{data: number, componentRef: React.RefObject<HTMLDivElement>}>(
{
data: NaN,
componentRef: idRef
}
)
<div ref={idRef} className="col-3">
<Id id={id} setId={setId}/>
</div>
CodePudding user response:
Issues
- You are setting the id passed to your props, with the setter function passed to your props: (This way every time this component renders, it sets the id, and since id is a prop, a re-render is triggered. Hence, an infinite loop of re-renders)
var idData = {...props.id};
useEffect(()=>{
props.setId(idData);
}, [idData]);
This is not necessary, since this is a controlled component, and you already passed the id to your child.
- You are modifying the props directly in your
modifyId
function. Instead, assign it to a state variable and make changes to that state variable
Understanding dependency array
In useEffect
the dependency array makes sure that, that particular useEffect triggers only depending on your dependency array
useEffect(() => console.log("run once"), [])
useEffect(() => console.log("run only id changes"), [id])
Solving your problem
Your problem is a little bit confusing to understand. Based on my understanding, you want to setId
when you modifyId
:
const Id : React.FC<Props> = ( props : Props ) => {
const {id: idData} = props
const [childId, setChildId] = useState({})
useEffect(() => {
if(idData) setChildId(idData)
}, [idData])
const generateNewId = () => {
setChildId(Math.floor( Math.random()*1000 ) 1)
}
const modifyId = (sign : string) => {
setChildId(id => sign === ' ' ? {...id, data: id.data 1} : {...id, data: id.data-1})
}
return(
<div>
<ReactTooltip id="counter-info">
<p>A random ID is generated each time you click refresh</p>
<p className="text-bold">What is ID?</p>
<p className="text-bold">Your ID is added to the back of your username, to always ensure that you have a unique username</p>
</ReactTooltip>
<h2>Your ID: {childId.data}
<span data-tip data-for="counter-info">
<FaQuestionCircle/>
</span>
</h2>
<button className="btn rounded-circle btn-dark" onClick={()=>modifyId("-")}>-</button>
<button className="btn rounded-circle btn-dark mr-3" onClick={()=>modifyId(" ")}> </button>
<button className="btn" onClick={()=>generateNewId()}><FaRedoAlt/></button>
</div>
)
}
export default Id;
PS: If you don't want to set a state variable, remove the useState
call and replace setChildId with prop.setId
CodePudding user response:
i see several mistakes in your code; passing an object as a dependency to useEffect
is not a good idea, objects in js are a reference type, when your component runs again (re-render), each round the idData
object will be recreated...
therefore, when the component runs again, the current object is unequal to the previous object that you passed to the useEffect
dependency array and useEffect
runs again... frequently, this process will be repeated constantly!
e.g:
{} === {} //flase!
in your case, you have to use setId
when you wanna set a new id... or if you have to initialize it, you can set an empty array as the useEffect
dependency.