#reactjs
Вопрос:
в моем компоненте react в моей консоли возникает следующая проблема
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)
Время, в течение которого отображается ошибка, увеличивается с каждой новой секундой, и та же ошибка создается снова. Я понятия не имею, почему, потому что я думал, что моя логика имеет смысл, как она создает бесконечный цикл?
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;
В приложении.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>
Ответ №1:
Проблемы
- Вы устанавливаете идентификатор, переданный вашему реквизиту, с помощью функции настройки, переданной вашему реквизиту: (Таким образом, каждый раз, когда этот компонент выполняет рендеринг, он устанавливает идентификатор, а поскольку идентификатор является реквизитом, запускается повторная рендеринг. Следовательно, бесконечный цикл рендеринга)
var idData = {...props.id};
useEffect(()=>{
props.setId(idData);
}, [idData]);
В этом нет необходимости, так как это контролируемый компонент, и вы уже передали идентификатор своему ребенку.
- Вы изменяете реквизиты непосредственно в своей
modifyId
функции. Вместо этого назначьте его переменной состояния и внесите изменения в эту переменную состояния
Понимание массива зависимостей
В useEffect
массиве зависимостей убедитесь, что этот конкретный эффект использования срабатывает только в зависимости от вашего массива зависимостей
useEffect(() => console.log("run once"), [])
useEffect(() => console.log("run only id changes"), [id])
Решение вашей проблемы
Ваша проблема немного запутана для понимания. Исходя из моего понимания, вы хотите, чтобы setId
, когда вы 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: Если вы не хотите устанавливать переменную состояния, удалите useState
вызов и замените setChildId на prop.setId
Ответ №2:
я вижу несколько ошибок в вашем коде; передача объекта в качестве зависимости useEffect
не является хорошей идеей, объекты в js являются ссылочным типом, когда ваш компонент запускается снова (повторный рендеринг), каждый раунд idData
объект будет воссоздан заново…
поэтому, когда компонент запускается снова, текущий объект не соответствует предыдущему объекту, который вы передали в массив useEffect
зависимостей, и useEffect
запускается снова… часто этот процесс будет повторяться постоянно!
например:
{} === {} //flase!
в вашем случае вы должны использовать setId
, когда хотите установить новый идентификатор… или, если вам нужно его инициализировать, вы можете установить пустой массив в качестве useEffect
зависимости.
Комментарии:
1. Как это может
{} !== {}
быть, если объекты имеют одинаковые ключи и значения? Пытаюсь понять2. @argonx вам нужно выполнить глубокое равенство для сравнения двух объектов, для этого вы можете использовать этот инструмент: npmjs.com/package/deep-equal