useEffect 与 useContext 陷入无限循环
Posted
技术标签:
【中文标题】useEffect 与 useContext 陷入无限循环【英文标题】:useEffect stuck in infinite loop with useContext 【发布时间】:2021-12-13 05:13:19 【问题描述】:我有一个功能组件Pets
,它显示了当前登录用户的所有宠物。在我的Pets
组件内部,我使用useEffect
和useState
来实现这一点。
const [pets, setPets] = useState([]);
useEffect(() =>
const fetchPets = async () =>
try
const data = await axios.get('/pet/mypets');
setPets(data.pets);
catch (error)
console.log(error);
;
fetchPets();
, []);
然后我逐行渲染表格中的所有宠物。问题是当我尝试使用useContext
执行此操作时,它最终陷入了无限循环。例如,我有一个名为petsContext
的文件,其代码如下...
import React from 'react';
const PetsContext = React.createContext(
pets: [],
onRegister: (first_name, last_name, breed, age, weight) => ,
onUpdate: (first_name, last_name, breed, age, weight) => ,
onDelete: () => ,
onFetch: () => ,
);
export default PetsContext;
我有一个petsProvider
文件,其中包含以下内容...
import React, useReducer from 'react';
import PetContext from './pets-context';
import axios from 'axios';
const initialState =
pets: [],
message: '',
messageType: '',
;
const petReducer = (state = initialState, action) =>
switch (action.type)
case 'FETCH_PETS_SUCCESS':
return
...state,
pets: action.payload,
;
case 'REGISTER_PET_SUCCESS':
if (state.pets === undefined)
state.pets = [];
return
...state,
pets: [action.payload.pet, ...state.pets],
message: action.payload.message,
;
case 'REGISTER_PET_FAIL':
return
...state,
message: 'Unable to register pet!',
;
default:
return initialState;
;
const PetProvider = (props) =>
const [petState, dispatchPetAction] = useReducer(petReducer,
petReducer,
initialState,
);
const registerHandler = async (first_name, last_name, breed, age, weight) =>
const config =
headers:
'Content-Type': 'application/json',
,
;
age = parseInt(age);
weight = parseFloat(weight);
const body = JSON.stringify( first_name, last_name, breed, age, weight );
try
const data = await axios.post('/pet/register', body, config);
dispatchPetAction( type: 'REGISTER_PET_SUCCESS', payload: data );
catch (error)
console.log(error.response);
// dispatchPetAction(
// type: 'REGISTER_PET_FAIL',
// payload:
// message: error.response.data.message,
// messageType: 'danger',
// ,
// );
;
const fetchHandler = async () =>
try
const data = await axios.get('/pet/mypets');
dispatchPetAction( type: 'FETCH_PETS_SUCCESS', payload: data.pets );
catch (err)
console.log(err);
;
return (
<PetContext.Provider
value=
pets: petState.pets,
onRegister: registerHandler,
onFetch: fetchHandler,
>
props.children
</PetContext.Provider>
);
;
export default PetProvider;
在我的Pets
组件中,而不是在我拥有以下内容之前显示的内容...
const petsContext = useContext(PetsContext);
useEffect(() =>
petsContext.onFetch();
, [petsContext]);
// now I want to just access my pets by `petsContext.pets`
// which works but ends up in an infinite loop and I am not sure why.
我该如何解决这个无限循环,为什么会这样?
【问题讨论】:
【参考方案1】:无限循环从您的上下文开始。
-
您的上下文值会为每次渲染创建。
由于您的上下文值已更改,因此效果调用 fetch
获取更新状态,继续触发渲染。因为触发了渲染,所以 First 将被触发。
修复它:
// wrap fetchHandler in useCallback
const fetchHandler = useCallback(async () =>
try
const data = await axios.get('/pet/mypets');
dispatchPetAction( type: 'FETCH_PETS_SUCCESS', payload: data.pets );
catch (err)
console.log(err);
, [dispatchPetAction]);
const onFetch = useContext(PetsContext);
// this effect should depend on onFetch, not petsContext
// now, it will only being call if dispatchPetAction is changed
// dispatchPetAction -> fetchHandler -> useEffect
useEffect(() =>
onFetch();
, [onFetch]);
https://reactjs.org/docs/hooks-reference.html#usecallback
【讨论】:
非常感谢。有效。我将阅读更多关于useCallback
实际工作原理的信息,以便我可以理解这一点。
很高兴为您提供帮助。 ?【参考方案2】:
useEffect
无限渲染的问题是由于依赖数组。如果您正在更新效果内的状态,但效果取决于您正在更新的状态,那么只要您的 petsContext
中的任何内容发生更改,就会调用该效果。
这意味着当上下文改变时调用效果,然后它改变上下文,它再次调用自身 ad infinitum 直到你得到一个 Stack Overflow (rimshot)。
要解决此问题,请确定您希望此效果依赖于什么,并将其最小化到该对象。 onFetch
在您的示例中被声明为一个空函数,所以我不能 100% 确定它在做什么,或者我会进一步建议您。
【讨论】:
好的。现在不在我的电脑上,但会试一试。我猜我可以使用useState
并且我的状态没有改变,那么我不应该处于这个无限循环中吗? onFetch 只是抓取一个宠物列表... [ fn: 'pets first name', ln: 'pets last name' ] 等等。 onFetch
在我的PetProvider
中指向fetchHandler
。本质上,它只是向我的 Flask 后端发出请求以从我的数据库中获取数据。以上是关于useEffect 与 useContext 陷入无限循环的主要内容,如果未能解决你的问题,请参考以下文章
如何将 React 钩子(useContext、useEffect)与 Apollo 反应钩子(useQuery)结合起来
了解函数化组件 HooksuseState,useEffect,useContext,useReducer
了解函数化组件 HooksuseState,useEffect,useContext,useReducer
从 REST API 填充上下文并在带有 useEffect 和 useContext 的 React 组件中使用它
useContext 未在子组件中显示更新的状态(当从全局文件中的 useEffect 挂钩中的 firebase 检索数据时)