React報(bào)錯(cuò)Too many re-renders解決
總覽
產(chǎn)生"Too many re-renders. React limits the number of renders to prevent an infinite loop"錯(cuò)誤有多方面的原因:
- 在一個(gè)組件的渲染方法中調(diào)用一個(gè)設(shè)置狀態(tài)的函數(shù)。
- 立即調(diào)用一個(gè)事件處理器,而不是傳遞一個(gè)函數(shù)。
- 有一個(gè)無限設(shè)置與重渲染的
useEffect鉤子。
這里有個(gè)示例來展示錯(cuò)誤是如何發(fā)生的:
import {useState} from 'react';
export default function App() {
const [counter, setCounter] = useState(0);
// ?? Too many re-renders. React limits the number
// of renders to prevent an infinite loop.
return (
<div>
<button onClick={setCounter(counter + 1)}>Increment</button>
<h1>Count: {counter}</h1>
</div>
);
}
上述代碼問題在于,我們?cè)?code>onClick事件處理器中立即調(diào)用了setCounter函數(shù)。
該函數(shù)是在頁面加載時(shí)立即被調(diào)用,而不是事件觸發(fā)后調(diào)用。
傳遞函數(shù)
為了解決該錯(cuò)誤,為onClick事件處理器傳遞函數(shù),而不是傳遞調(diào)用函數(shù)的結(jié)果。
import {useState} from 'react';
export default function App() {
const [counter, setCounter] = useState(0);
return (
<div>
<button onClick={() => setCounter(counter + 1)}>Increment</button>
<h1>Count: {counter}</h1>
</div>
);
}
現(xiàn)在,我們?yōu)槭录幚砥鱾鬟f了函數(shù),而不是當(dāng)頁面加載時(shí)調(diào)用setCounter方法。
如果該方法在頁面加載時(shí)被調(diào)用,就會(huì)觸發(fā)一個(gè)setState動(dòng)作,組件就會(huì)無限重新渲染。
如果我們?cè)噲D立即設(shè)置一個(gè)組件的狀態(tài),而不使用一個(gè)條件或事件處理器,也會(huì)發(fā)生這個(gè)錯(cuò)誤。
import {useState} from 'react';
export default function App() {
const [counter, setCounter] = useState(0);
// ?? Too many re-renders. React limits the number
// of renders to prevent an infinite loop.
setCounter(counter + 1);
return (
<div>
<h1>Count: {counter}</h1>
</div>
);
}
問題在于,setCounter函數(shù)在組件渲染時(shí)被調(diào)用、更新狀態(tài),并導(dǎo)致重新渲染,而且是無限重新渲染。
你可以通過向useState()鉤子傳遞一個(gè)初始值或一個(gè)函數(shù)來初始化狀態(tài),從而解決這個(gè)錯(cuò)誤。
import {useState} from 'react';
export default function App() {
const [counter, setCounter] = useState(() => 100 + 100);
return (
<div>
<h1>Count: {counter}</h1>
</div>
);
}
我們向useState方法傳遞了一個(gè)函數(shù)。這個(gè)函數(shù)只會(huì)在組件第一次渲染時(shí)被調(diào)用,并且會(huì)計(jì)算出初始狀態(tài)。你也可以直接向useState方法傳遞一個(gè)初始值。
另外,你也可以像前面的例子那樣使用一個(gè)條件或事件處理器。
import {useState} from 'react';
export default function App() {
const [counter, setCounter] = useState(0);
// ??? your condition here
if (Math.random() > 0.5) {
setCounter(counter + 1);
}
return (
<div>
<h1>Count: {counter}</h1>
</div>
);
}
如果你像上面的例子那樣使用一個(gè)條件,請(qǐng)確保該條件不總是返回一個(gè)真值,因?yàn)檫@將導(dǎo)致無限的重新渲染循環(huán)。
"Too many re-renders. React limits the number of renders to prevent an infinite loop"錯(cuò)誤也會(huì)在使用useEffect方法時(shí)發(fā)生,該方法的依賴會(huì)導(dǎo)致無限重新渲染。
import {useEffect, useState} from 'react';
export default function App() {
const [counter, setCounter] = useState(0);
useEffect(() => {
// ?? Too many re-renders. React limits the number
// of renders to prevent an infinite loop.
setCounter(counter + 1);
}); // ??? forgot to pass dependency array
return (
<div>
<h1>Count: {counter}</h1>
</div>
);
}
上述代碼問題在于,我們沒有為useEffect鉤子傳遞依賴數(shù)組。
這意味著該鉤子會(huì)在每次渲染時(shí)運(yùn)行,它會(huì)更新組件的狀態(tài),然后無限重新運(yùn)行。
傳遞依賴
解決該錯(cuò)誤的一種辦法是,為useEffect提供空數(shù)組作為第二個(gè)參數(shù)。
import {useEffect, useState} from 'react';
export default function App() {
const [counter, setCounter] = useState(0);
useEffect(() => {
setCounter(counter + 1);
}, []); // ??? empty dependencies array
return (
<div>
<h1>Count: {counter}</h1>
</div>
);
}
如果你為
useEffect方法傳遞空數(shù)組依賴作為第二個(gè)參數(shù),該方法只在組件的初始渲染時(shí)運(yùn)行。
該代碼將計(jì)數(shù)器遞增到1,并且不再運(yùn)行,無論App組件是否被重新渲染。
如果你必須指定一個(gè)依賴來無限地重新渲染你的組件,試著尋找一個(gè)可以防止這種情況的條件。
import {useEffect, useState} from 'react';
export default function App() {
const [counter, setCounter] = useState(0);
useEffect(() => {
// ??? some condition here
if (Math.random() > 0.5) {
setCounter(counter + 1);
}
}, [counter]);
return (
<div>
<h1>Count: {counter}</h1>
</div>
);
}
有可能是某些邏輯決定了狀態(tài)是否應(yīng)該被更新,而狀態(tài)不應(yīng)該在每次重新渲染時(shí)被設(shè)置。
確保你沒有使用一個(gè)在每次渲染時(shí)都不同的對(duì)象或數(shù)組作為useEffect鉤子的依賴。
import {useEffect, useState} from 'react';
export default function App() {
const [address, setAddress] = useState({country: '', city: ''});
const obj = {country: 'Chile', city: 'Santiago'};
useEffect(() => {
// ?? Too many re-renders. React limits the number
// of renders to prevent an infinite loop.
setAddress(obj);
console.log('useEffect called');
}, [obj]);
return (
<div>
<h1>Country: {address.country}</h1>
<h1>City: {address.city}</h1>
</div>
);
}
問題在于,在JavaScript中,對(duì)象是通過引用進(jìn)行比較的。obj變量存儲(chǔ)了一個(gè)具有相同鍵值對(duì)的對(duì)象,但每次渲染時(shí)的引用不同(在內(nèi)存中的位置不同)。
移入依賴
解決該錯(cuò)誤的一種辦法是,把這個(gè)對(duì)象移到useEffect鉤子里面,這樣我們就可以把它從依賴數(shù)組中移除。
import {useEffect, useState} from 'react';
export default function App() {
const [address, setAddress] = useState({country: '', city: ''});
useEffect(() => {
// ??? move object inside of useEffect
// and remove it from dependencies array
const obj = {country: 'Chile', city: 'Santiago'};
setAddress(obj);
console.log('useEffect called');
}, []);
return (
<div>
<h1>Country: {address.country}</h1>
<h1>City: {address.city}</h1>
</div>
);
}
傳遞對(duì)象屬性
另一個(gè)解決方案是將對(duì)象的屬性傳遞給依賴數(shù)組。
import {useEffect, useState} from 'react';
export default function App() {
const [address, setAddress] = useState({country: '', city: ''});
const obj = {country: 'Chile', city: 'Santiago'};
useEffect(() => {
setAddress({country: obj.country, city: obj.city});
console.log('useEffect called');
// ??? object properties instead of the object itself
}, [obj.country, obj.city]);
return (
<div>
<h1>Country: {address.country}</h1>
<h1>City: {address.city}</h1>
</div>
);
}
現(xiàn)在React不是在測試一個(gè)對(duì)象是否發(fā)生了變化,而是在測試obj.country和obj.city字符串在渲染之間是否發(fā)生了變化。
記憶值
另外,我們可以使用useMemo鉤子來獲得一個(gè)在不同渲染之間不會(huì)改變的記憶值。
import {useEffect, useMemo, useState} from 'react';
export default function App() {
const [address, setAddress] = useState({country: '', city: ''});
// ??? get memoized value
const obj = useMemo(() => {
return {country: 'Chile', city: 'Santiago'};
}, []);
useEffect(() => {
setAddress(obj);
console.log('useEffect called');
}, [obj]);
return (
<div>
<h1>Country: {address.country}</h1>
<h1>City: {address.city}</h1>
</div>
);
}
我們將對(duì)象的初始化包裹在useMemo鉤子里面,以獲得一個(gè)不會(huì)在渲染之間改變的記憶值。
我們傳遞給useMemo鉤子的第二個(gè)參數(shù)是一個(gè)依賴數(shù)組,它決定了我們傳遞給useMemo的回調(diào)函數(shù)何時(shí)被重新運(yùn)行。
需要注意的是,數(shù)組在JavaScript中也是通過引用進(jìn)行比較的。所以一個(gè)具有相同值的數(shù)組也可能導(dǎo)致你的useEffect鉤子被無限次觸發(fā)。
import {useEffect, useMemo, useState} from 'react';
export default function App() {
const [nums, setNums] = useState([1, 2, 3]);
const arr = [4, 5, 6];
useEffect(() => {
// ?? Too many re-renders. React limits the number
// of renders to prevent an infinite loop.
setNums(arr);
console.log('useEffect called');
}, [arr]);
return <div>{nums[0]}</div>;
}
數(shù)組在重新渲染之間存儲(chǔ)相同的值,但指向內(nèi)存中的不同位置,并且在每次組件重新渲染時(shí)有不同的引用。
在處理數(shù)組時(shí),我們用于對(duì)象的方法同樣有效。例如,我們可以使用useMemo鉤子來獲得一個(gè)在渲染之間不會(huì)改變的記憶值。
import {useEffect, useMemo, useState} from 'react';
export default function App() {
const [nums, setNums] = useState([1, 2, 3]);
const arr = useMemo(() => {
return [4, 5, 6];
}, []);
useEffect(() => {
setNums(arr);
console.log('useEffect called');
}, [arr]);
return <div>{nums[0]}</div>;
}
我們將數(shù)組的初始化包裹在useMemo鉤子里面,以獲得一個(gè)不會(huì)在不同渲染之間改變的記憶值。
以上就是React報(bào)錯(cuò)Too many re-renders解決的詳細(xì)內(nèi)容,更多關(guān)于React報(bào)錯(cuò)Too many re-renders的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- React報(bào)錯(cuò)Type '() => JSX.Element[]' is not assignable to type FunctionComponent
- React報(bào)錯(cuò)Element type is invalid解決案例
- React報(bào)錯(cuò)Function?components?cannot?have?string?refs
- 解決React報(bào)錯(cuò)Property 'X' does not exist on type 'HTMLElement'
- Can't?perform?a?React?state?update?on?an?unmounted?component報(bào)錯(cuò)解決
- Objects are not valid as a React child報(bào)錯(cuò)解決
- 解決React報(bào)錯(cuò)No duplicate props allowed
- 解決React報(bào)錯(cuò)React.Children.only expected to receive single React element child
相關(guān)文章
ReactNative短信驗(yàn)證碼倒計(jì)時(shí)控件的實(shí)現(xiàn)代碼
本篇文章主要介紹了ReactNative短信驗(yàn)證碼倒計(jì)時(shí)控件的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
ForwardRef?useImperativeHandle方法demo
這篇文章主要為大家介紹了ForwardRef?useImperativeHandle方法demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
react-redux中connect()方法詳細(xì)解析
connect()是React-redux中的核心方法之一,它將react組件預(yù)redux中的Store真正連接在一起,下面這篇文章主要給大家介紹了react-redux中connect()方法的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-05-05
一文詳解React Redux設(shè)計(jì)思想與工作原理
最近看項(xiàng)目中使用了?Redux,?便嘗試了解一波?Redux?的設(shè)計(jì)思想與工作原理,所以本文詳細(xì)的給大家介紹了Redux設(shè)計(jì)思想與工作原理,需要的朋友可以參考下2023-09-09
webpack打包react項(xiàng)目的實(shí)現(xiàn)方法
這篇文章主要介紹了webpack打包react項(xiàng)目的實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06

