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>
}