Reactjs 反应挂钩及;上下文:在具有useEffect的子组件中使用上下文时出错
我为上下文创建了一个Reactjs 反应挂钩及;上下文:在具有useEffect的子组件中使用上下文时出错,reactjs,react-hooks,react-context,Reactjs,React Hooks,React Context,我为上下文创建了一个react函数组件,如下所示: const ItemContext=createContext() 因此,组件应该在更改id时执行API调用。但我在控制台中发现了这个错误: React Hook useEffect缺少依赖项:“itemContext.actions”。包括它或删除依赖项数组react hooks/dep 但是,如果我将其添加到依赖项数组中,我会在服务器上得到一个永无止境的API调用循环。所以我不知道该怎么办。或者如果我走错了方向。谢谢 ==更新==== 下面
react
函数组件,如下所示:
const ItemContext=createContext()
因此,组件应该在更改id
时执行API调用。但我在控制台中发现了这个错误:
React Hook useEffect缺少依赖项:“itemContext.actions”。包括它或删除依赖项数组react hooks/dep
但是,如果我将其添加到依赖项数组中,我会在服务器上得到一个永无止境的API调用循环。所以我不知道该怎么办。或者如果我走错了方向。谢谢
==更新====
下面是一个JSFIDLE来试用它:
(仅供参考,我意识到该警告不在控制台中,因为它不起毛)您可以利用
useCallback
来获取方法,该方法
…并将其放入useffect
:
...
const { actions, state } = useContext(ItemContext)
useEffect(() => {
actions.findById(id)
}, [id, actions.findById])
...
工作示例:
您的问题与反复调用自定义挂钩有关,因为这是一个正常的函数,在整个渲染过程中,React不会“保存”
更新 我最初的回答修复了无限循环。 您的问题还与您使用上下文的方式有关,因为它一次又一次地重新创建上下文的域对象(
操作
,状态
,…)
下面是您将上下文拆分为state
和dispatch
的精彩示例,我对此建议不多。这将修复无限循环,并提供更清晰的上下文使用结构。请注意,根据我的原始答案,我仍然在使用fetch函数的useCallback
:
完整代码沙盒
App.js
import React,{useffect,useCallback}来自“React”;
导入“/styles.css”;
从“/item context”导入{useItemState,ItemProvider,useItemDispatch}”;
常量SmallComponent=()=>{
常数id=5;
const{username}=useItemState();
const dispatch=useItemDispatch();
const fetchUsername=useCallback(异步()=>{
const response=等待获取(
"https://jsonplaceholder.typicode.com/users/“+id
);
const user=wait response.json();
分派({type:“setUsername”,usernamedidated:user.name});
},[发送];
useffect(()=>{
fetchUsername();
},[fetchUsername]);
返回(
获取的用户名:
{用户名| |“未设置”}
);
};
导出默认函数App(){
返回(
);
}
item-context.js
从“React”导入React;
const ItemStateContext=React.createContext();
const ItemDispatchContext=React.createContext();
函数项减速器(状态、操作){
开关(动作类型){
案例“setUsername”:{
返回{…状态,用户名:action.usernameUpdated};
}
默认值:{
抛出新错误(`Unhandled action type:${action.type}`);
}
}
}
函数项提供程序({children}){
const[state,dispatch]=React.useReducer(itemReducer{
用户名:“初始用户名”
});
返回(
{儿童}
);
}
函数useItemState(){
const context=React.useContext(ItemStateContext);
如果(上下文===未定义){
抛出新错误(“useItemState必须在CountProvider中使用”);
}
返回上下文;
}
函数useItemDispatch(){
const context=React.useContext(ItemDispatchContext);
如果(上下文===未定义){
抛出新错误(“useItemDispatch必须在CountProvider中使用”);
}
返回上下文;
}
导出{ItemProvider,useItemState,useItemDispatch};
当我开始使用带有钩子的上下文时,这两篇博客文章对我帮助很大:
您可以将
useCallback
用于获取方法,该方法
…并将其放入useffect
:
...
const { actions, state } = useContext(ItemContext)
useEffect(() => {
actions.findById(id)
}, [id, actions.findById])
...
工作示例:
您的问题与反复调用自定义挂钩有关,因为这是一个正常的函数,在整个渲染过程中,React不会“保存”
更新 我最初的回答修复了无限循环。 您的问题还与您使用上下文的方式有关,因为它一次又一次地重新创建上下文的域对象(
操作
,状态
,…)
下面是您将上下文拆分为state
和dispatch
的精彩示例,我对此建议不多。这将修复无限循环,并提供更清晰的上下文使用结构。请注意,根据我的原始答案,我仍然在使用fetch函数的useCallback
:
完整代码沙盒
App.js
import React,{useffect,useCallback}来自“React”;
导入“/styles.css”;
从“/item context”导入{useItemState,ItemProvider,useItemDispatch}”;
常量SmallComponent=()=>{
常数id=5;
const{username}=useItemState();
const dispatch=useItemDispatch();
const fetchUsername=useCallback(异步()=>{
const response=等待获取(
"https://jsonplaceholder.typicode.com/users/“+id
);
const user=wait response.json();
分派({type:“setUsername”,usernamedidated:user.name});
},[发送];
useffect(()=>{
fetchUsername();
},[fetchUsername]);
返回(
获取的用户名:
{用户名| |“未设置”}
);
};
导出默认函数App(){
返回(
);
}
item-context.js
从“React”导入React;
const ItemStateContext=React.createContext();
const ItemDispatchContext=React.createContext();
函数项减速器(状态、操作){
开关(动作类型){
案例“setUsername”:{
返回{…状态,用户名:action.usernameUpdated};
}
默认值:{
抛出新错误(`Unhandled action t
const findById = useCallback((args = {}) => {
fetch("http://....", { method: "POST" }).then(newItem => {
setItem(newItem);
});
}, []);
...
const { actions, state } = useContext(ItemContext)
useEffect(() => {
actions.findById(id)
}, [id, actions.findById])
...
import React, { useEffect, useCallback } from "react";
import "./styles.css";
import { useItemState, ItemProvider, useItemDispatch } from "./item-context";
const SmallComponent = () => {
const id = 5;
const { username } = useItemState();
const dispatch = useItemDispatch();
const fetchUsername = useCallback(async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users/" + id
);
const user = await response.json();
dispatch({ type: "setUsername", usernameUpdated: user.name });
}, [dispatch]);
useEffect(() => {
fetchUsername();
}, [fetchUsername]);
return (
<div>
<h4>Username from fetch:</h4>
<p>{username || "not set"}</p>
</div>
);
};
export default function App() {
return (
<div className="App">
<ItemProvider>
<SmallComponent />
</ItemProvider>
</div>
);
}
import React from "react";
const ItemStateContext = React.createContext();
const ItemDispatchContext = React.createContext();
function itemReducer(state, action) {
switch (action.type) {
case "setUsername": {
return { ...state, username: action.usernameUpdated };
}
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
}
function ItemProvider({ children }) {
const [state, dispatch] = React.useReducer(itemReducer, {
username: "initial username"
});
return (
<ItemStateContext.Provider value={state}>
<ItemDispatchContext.Provider value={dispatch}>
{children}
</ItemDispatchContext.Provider>
</ItemStateContext.Provider>
);
}
function useItemState() {
const context = React.useContext(ItemStateContext);
if (context === undefined) {
throw new Error("useItemState must be used within a CountProvider");
}
return context;
}
function useItemDispatch() {
const context = React.useContext(ItemDispatchContext);
if (context === undefined) {
throw new Error("useItemDispatch must be used within a CountProvider");
}
return context;
}
export { ItemProvider, useItemState, useItemDispatch };
const ItemProvider = ({ children }) => {
const [item, setItem] = useState(null)
const findById = useCallback((args = {}) => {
fetch('http://....', { method: 'POST' }).then((newItem) => setItem(newItem))
}, []);
return (
<ItemContext.Provider value={{ actions: { findById }, state: { item } }}>
{children}
</ItemContext.Provider>
)
}
const smallComponent = () => {
const { id } = useParams()
const { actions } = useContext(ItemContext)
useEffect(() => {
itemContext.actions.findById(id)
}, [actions.findById, id])
return <div>info here</div>
}