Reactjs 自定义挂钩导致无限循环
Reactjs Custom hook causing an infinite loop
谁能帮忙解释一下为什么这段代码会导致无限循环,请问修复它的最佳方法是什么,谢谢。
我假设它是因为 App 组件中的 useEffect 导致重新渲染,然后 useState 也会导致渲染,从而导致无限循环。我想我不明白 useEffect 和 useState 是如何正常工作的。
import "./styles.css";
import React, { useEffect, useState } from "react";
interface IObject {
isLoading: boolean;
isError: boolean;
data: any[] | any;
}
function useHook1(): IObject {
console.log("hook 1 too many re-renders?");
return { isLoading: false, isError: false, data: [] };
}
function useHook2(): IObject {
const result = { isLoading: false, isError: false, data: "testing" };
console.log("hook 2 too many re-renders?");
return result;
}
export default function App() {
const { isLoading, isError, data } = useHook1();
const testResult = useHook2();
const [state, setState] = useState();
useEffect(() => {
console.log("inside useEffect within App")
setState(testResult.data)
}, [testResult])
console.log("too many re-renders?");
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<p>{testing}</p>
</div>
);
}
useHook2
return每次运行都会生成一个新对象。这意味着 testResult
在每次渲染时都是 new/changed,并且只要 testResult
发生变化,您的 useEffect
就会运行。所以:
您的效果会更新状态,这会导致 re-render。
在重新渲染时,useHook2
被调用并且 testResult
被更新。
testResult
改变了,所以你的效果再次运行,你 return 到第 1 步,进入无限循环。
如果我明白你想做什么,解决方案是在自定义挂钩中进行状态管理:
function useHook1() {
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const [data, setData] = useState([])
return {
isLoading,
isError,
data,
};
}
您可以通过将重复的 useState
用法折叠成一个 useReducer:
来稍微简化一下
function useHook1() {
const [state, dispatch] = useReducer(
(state, action) => ({...state, ...action}),
{ isLoading: false, isError: false, data: [] }
);
return { ...state };
}
如果您需要从挂钩外部触发更新,您也可以使其可用:
function useHook1() {
const [state, dispatch] = useReducer(
(state, action) => ({...state, ...action}),
{ isLoading: false, isError: false, data: [] }
);
return { ...state, dispatch };
}
export default function App () {
const [{ isLoading, isError, data }, setState] = useHook1();
return isLoading ? <div>Loading</div> : (
<div onClick={() => dispatch({ data: ["new data"]})}>...</div>
)
}
谁能帮忙解释一下为什么这段代码会导致无限循环,请问修复它的最佳方法是什么,谢谢。
我假设它是因为 App 组件中的 useEffect 导致重新渲染,然后 useState 也会导致渲染,从而导致无限循环。我想我不明白 useEffect 和 useState 是如何正常工作的。
import "./styles.css";
import React, { useEffect, useState } from "react";
interface IObject {
isLoading: boolean;
isError: boolean;
data: any[] | any;
}
function useHook1(): IObject {
console.log("hook 1 too many re-renders?");
return { isLoading: false, isError: false, data: [] };
}
function useHook2(): IObject {
const result = { isLoading: false, isError: false, data: "testing" };
console.log("hook 2 too many re-renders?");
return result;
}
export default function App() {
const { isLoading, isError, data } = useHook1();
const testResult = useHook2();
const [state, setState] = useState();
useEffect(() => {
console.log("inside useEffect within App")
setState(testResult.data)
}, [testResult])
console.log("too many re-renders?");
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<p>{testing}</p>
</div>
);
}
useHook2
return每次运行都会生成一个新对象。这意味着 testResult
在每次渲染时都是 new/changed,并且只要 testResult
发生变化,您的 useEffect
就会运行。所以:
您的效果会更新状态,这会导致 re-render。
在重新渲染时,
useHook2
被调用并且testResult
被更新。testResult
改变了,所以你的效果再次运行,你 return 到第 1 步,进入无限循环。
如果我明白你想做什么,解决方案是在自定义挂钩中进行状态管理:
function useHook1() {
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const [data, setData] = useState([])
return {
isLoading,
isError,
data,
};
}
您可以通过将重复的 useState
用法折叠成一个 useReducer:
function useHook1() {
const [state, dispatch] = useReducer(
(state, action) => ({...state, ...action}),
{ isLoading: false, isError: false, data: [] }
);
return { ...state };
}
如果您需要从挂钩外部触发更新,您也可以使其可用:
function useHook1() {
const [state, dispatch] = useReducer(
(state, action) => ({...state, ...action}),
{ isLoading: false, isError: false, data: [] }
);
return { ...state, dispatch };
}
export default function App () {
const [{ isLoading, isError, data }, setState] = useHook1();
return isLoading ? <div>Loading</div> : (
<div onClick={() => dispatch({ data: ["new data"]})}>...</div>
)
}