React: React Hook useEffect 缺少依赖项:'xxx'?

创建于 2019-06-12  ·  69评论  ·  资料来源: facebook/react

嗨,我最近在研究 React Hook,它很棒。

但是我有一个问题,我在 React Hook 文档和 google 中没有找到合适的答案来解决我的问题。

当我在useEffect和函数组件中一起使用一个函数时,遇到了没有在useEffect中指定依赖的警告,如下图:

image

有什么办法可以解决我的问题吗? 我想了很久。

目前的行为是什么?

React Hook useEffect has a missing dependency: 'setCenterPosition'. Either include it or remove the dependency array. (react-hooks/exhaustive-deps)

代码沙盒中的迷你重现:
https://codesandbox.io/s/trusting-kowalevski-oet4b

任何解决方案,谢谢。

代码

function App() {
  const [elePositionArr, setElePositionArr] = useState([
    { left: 0, top: 0 },
    { left: 0, top: 0 },
    { left: 0, top: 0 }
  ]);
  const stageRef = useRef(null);
  const Stage = useRef({ w: 0, h: 0 });
  const currentIndex = useRef(0);

  useEffect(() => {
    // Record the size of the stage
    const stageW = stageRef.current.scrollWidth;
    const stageH = stageRef.current.scrollHeight;

    Stage.current = { w: stageW, h: stageH };

    const index = Math.floor(Math.random() * 3);
    currentIndex.current = index;
    setCenterPosition(index);
  }, []);

  // Centering a block element
  function setCenterPosition(index) {
    let cacheEle = elePositionArr;
    // calc center postion
    const centerOfLeft = Stage.current.w / 2 - 25;
    const centerOfTop = Stage.current.h / 2 - 25;

    cacheEle = cacheEle.map((item, i) => {
      const randomWNum = Math.floor(Math.random() * Stage.current.w) - 50;
      const randomHNum = Math.floor(Math.random() * Stage.current.h) - 50;
      const randomLeft = randomWNum <= 50 ? 50 : randomWNum;
      const randomTop = randomHNum <= 50 ? 50 : randomHNum;
      let newItem;

      if (index === i) {
        newItem = { left: centerOfLeft, top: centerOfTop };
      } else {
        newItem = { left: randomLeft, top: randomTop };
      }

      return newItem;
    });

    setElePositionArr(cacheEle);
  }

  function handleClickLi(index) {
    if (currentIndex.current !== index) {
      setCenterPosition(index);
      currentIndex.current = index;
    }
  }

  return (
    <div className="container">
      <div className="stage" ref={stageRef}>
        {elePositionArr.map((item, index) => (
          <div className="ele" key={index} style={item}>
            {index}
          </div>
        ))}
      </div>
      <ul className="nav">
        {elePositionArr.map((item, index) => (
          <li
            className={currentIndex.current === index ? "active-li" : ""}
            onClick={() => {
              handleClickLi(index);
            }}
            key={"li" + index}
          >
            {index}
          </li>
        ))}
      </ul>
    </div>
  );
}

最有用的评论

当成千上万的开发人员面临同样的问题并且 React 开发团队关闭线程并忽略每个人时:
8d6

所有69条评论

这应该回答你的问题:

https://reactjs.org/docs/hooks-faq.html#is -it-safe-to-omit-functions-from-the-list-of-dependencies

这应该回答你的问题:

https://reactjs.org/docs/hooks-faq.html#is -it-safe-to-omit-functions-from-the-list-of-dependencies

@gaearon
没有解决我的问题,我的问题比较特殊,我的run函数setElePositionArr想在componentDidMount和handleClick上触发,但是React Hook函数不符合我的要求,出现警告。

我的问题是:React Hook 不能调用useEffect 和函数组件中的一个函数来进行setState 操作?

保持开放,以便我们可以回答第二个问题。

我有相同的 ESLint 警告,但在我的情况下,我想在安装组件时进行异步调用。

const ManageTagsModalBody: React.FC<IProps> = ({ children, getTags }) => {
  useEffect(() => {
    getTags();
  }, []);

  return <div>{children}</div>;
};
Line 12:  React Hook useEffect has a missing dependency: 'getTags'. Either include it or remove the dependency array. If 'getTags' changes too often, find the parent component that defines it and wrap that definition in useCallback  react-hooks/exhaustive-deps 

ManageTagsModalBody不依赖于任何数据,它仅用作在组件呈现时加载数据的包装器。 值得注意的是children组件正在使用getTags获取的数据。

我不确定要添加到依赖项列表中的内容。

我也对解释感兴趣。
React Hook useEffect 缺少依赖项:'dispatch'。

  useEffect(() => {
    axios.get('http://localhost:8000/api/future-registrations')
      .then((result) => {
        dispatch(initRegistrationsAction(result.data));
      });
  }, []);

同样的问题在这里。 奇怪的规则。

同样在这里。 就像https://github.com/facebook/react/issues/15865#issuecomment -506503377 一样简单:

  useEffect(() => {
    dispatch(fetchPosts());
  }, []);

有没有办法抑制这个警告?

正如警告消息中所建议的那样,您可以这样做

const initFetch = useCallback(() => {
    dispatch(fetchPosts());
  }, [dispatch]);

  useEffect(() => {
    initFetch();
  }, [initFetch]);

@fayway那么当 initFetch 和 dispatch 值改变时,useEffect 的回调将被执行。
他想执行一次回调

@shkyung在我的情况下, initFetch不会改变原因(存储。) dispatch也不会

也许它可以是这样的

const setCenterPosition = useRef(null)
setCenterPosition.current = useCallback( ()=>{} ,[deps])

effect(()=>{ setCenterPosition.current() },[setCenterPosition,otherDeps])

坦率地说,这种变化似乎有点荒谬,而且适得其反。 而是回滚到较早的版本,让您仔细考虑这一点。

有同样的问题。 我只希望我的 fetch 在 componentDidMount 上触发。 将依赖项添加到数组会导致重复命中端点。

React Hook useEffect 缺少一个依赖项:'dispatch'。

const [state, dispatch] = useReducer(reducer, initialState);
const { count, step } = state;

useEffect(() => {
  const id = setInterval(() => {
    dispatch({ type: 'tick' }); // Instead of setCount(c => c + step);
  }, 1000);
  return () => clearInterval(id);
}, [dispatch]);

这似乎是一个非常常见的用例 - 我在当前项目中遇到过。

您只想运行一次效果挂钩,但存在依赖性,尽管您只关心启动时的状态。 目前使用
// eslint-disable-next-line react-hooks/exhaustive-deps ,
关闭 linting 规则,但在这种情况下不必这样做就好了。

这似乎是一个非常常见的用例 - 我在当前项目中遇到过。

您只想运行一次效果挂钩,但存在依赖性,尽管您只关心启动时的状态。 目前使用
// eslint-disable-next-line react-hooks/exhaustive-deps ,
关闭 linting 规则,但在这种情况下不必这样做就好了。

关闭Lint只是一个临时解决方案,解决这个问题的关键还不够了解hook。

同样的问题在这里。 我只想在 useEffect() 中触发一个函数,将 [] 作为第二个参数做我想要的,但它一直发出这个警告。

如此奇怪的警告,因为直到今天我才在新项目中看到它。 它破坏了在组件安装后只调用一次的用例。 除了忽略 lint 警告之外,似乎没有任何解决方法。

我想在这里投入我的两分钱。 特别是使用 Axios 取消网络请求。 如果需要,将创建一个单独的问题,但觉得这个问题直接影响了我自己。 所以我的 useApi 钩子如下 -

import axios, { AxiosInstance } from "axios";
import axiosRetry from "axios-retry";
import { FetchEnv } from "../../utilities";
import { useStorage } from "../useStorage";

export const useApi = ({ isAuth = false, retries = 3 } = {}) => {
  const cancelToken = axios.CancelToken.source();
  const { getItem } = useStorage();

  const getBaseUrl = () => {
    return FetchEnv({
      defaultValue: "http://api.example.com",
      key: "API_URI"
    });
  };

  const authorizedClient = (client: AxiosInstance) => {
    const session = getItem("SESSION", "sessionStorage");
    const hasAccessToken = Boolean(session.tokens.accessToken);

    if (isAuth && !hasAccessToken) {
      console.error("No access token found to initiate authorized request.");
      return client;
    } else {
      client.defaults.headers[
        "Authorization"
      ] = `Bearer ${session.tokens.accessToken}`;
      return client;
    }
  };

  const buildFetch = (): AxiosInstance => {
    const client = axios.create({
      baseURL: getBaseUrl(),
      cancelToken: cancelToken.token
    });
    axiosRetry(client, { retries });
    return isAuth ? authorizedClient(client) : client;
  };

  return {
    cancelRequest: cancelToken.cancel,
    fetch: buildFetch()
  };
};

要在组件中使用此钩子,只需将其定义如下,然后它可用于在挂载时、在 useEffect 内、作为事件处理程序的一部分等进行经过身份验证和未经身份验证的调用。cancelRequest 是生成的唯一取消令牌与相对于该组件的“获取”axios 实例配对。

const { cancelRequest, fetch } = useApi();

如果由于任何原因此组件卸载,则调用以下内容:

  useEffect(() => {
    return () => {
      cancelRequest("Auth network request cancelled");
    };
  }, []);

现在它目前完美运行。 但是,警告在应用时(将 cancelRequest 作为 useEffect 依赖项)会在调用 fetch 方法时立即取消网络请求。 任何建议将不胜感激,但目前看来唯一的解决方案是 ts-lint 禁用此向前推进...

我认为这里的问题可能是我们思考钩子的方式。 @luojinghui评论表明,这远远超出了人们的想象。 或许我们需要换个思路? 我已经能够通过一些重构在我的一个钩子中消除这个问题。 结果代码更清晰。

这让我想知道,在上面@Stoner609例子中,计时代码是否应该在 useCallback 钩子中? 尽管如此,将一个函数(应该是静态的)作为依赖仍然很奇怪。

我认为这种情况应该是某种例外

如果您只想在组件加载时运行一个函数并接受参数,那么您可以使用 useRef 来避免警告。 像这样的工作:

  const InitialPropA= useRef(propA);
  useEffect(() => {
    myFunction(InitialPropA.current);
  }, [myFunction, InitialPropA]);

正如警告消息中所建议的那样,您可以这样做

const initFetch = useCallback(() => {
    dispatch(fetchPosts());
  }, [dispatch]);

  useEffect(() => {
    initFetch();
  }, [initFetch]);

尽管我很喜欢这个诙谐的回复,但这通常是我的代码中发生的事情。
我不想收到所有关于派遣失踪的讨厌警告。

我很想知道在这一系列的谜团中究竟发生了什么以及为什么发生:)

正如警告消息中所建议的那样,您可以这样做

const initFetch = useCallback(() => {
    dispatch(fetchPosts());
  }, [dispatch]);

  useEffect(() => {
    initFetch();
  }, [initFetch]);

这也可能会在 API 之类的情况下造成内存泄漏,它将在无限循环中进行调用。

真的需要解决这个问题。 如果您只想在useEffect()触发一个函数,它会不断给我错误并且不知道如何解决它。

面对同样的错误——

React Hook useEffect 缺少依赖项:'props'。 然而,'props' 会在任何props 改变时改变,所以首选的解决方法是在 useEffect 调用之外解构 'props' 对象,并在 useEffect react-hooks/exhaustive-deps 中引用那些特定的 props

代码 -

  useEffect(() => {
    props.dispatch(searchMediaAction("rain"));
  }, []);

@luojinghui :如果您解决了 lint 错误,请告诉我

同样的错误

同样的问题。 我想模仿componentDidMount的行为; 我对警告不感兴趣; 我不想在发生任何变化时重新运行钩子

useEffect()的第二个参数为[] ,我建议不要显示此警告,b/c 在这种情况下,开发人员知道此效果只会运行一次,因此 _wants_ 使用初始道具值,并不关心道具在后续渲染中是否发生变化。

情况1:
如果我们希望函数在初始化时以及指定参数值发生变化时运行(如 lint 警告所示),那么我们将这些参数传递到数组中。

案例2:
如果我们希望函数在初始化时

在案例 2 中,我们实际上是根据 React Hook 文档规范监听 componentDidMount 事件。 lint 错误与 React Hook 文档和开发人员期望不一致。

为什么这是关闭的?

任何解决方案?

@kennylbj

在尝试了一系列不同的建议之后,这最终对我有用。 我更喜欢不使用 eslint-disable,同时尝试让代码对新手来说相对简单。 这种方法允许您传递调度函数而无需在 useCallback 内部调用它。 希望这可以帮助。

```javascript
// ... 内部函数组件
const { dispatchFunc } = customHook();
const memoizeDispatchFunc = useCallback(dispatchFunc, []);

useEffect(() => {
memoizeDispatchFunc();
}, [memoizeDispatchFunc]);
````

我们可以删除空数组吗? 这样做似乎有效(代码仍按预期运行,警告消失),但我不知道是否有任何理由我们不应该这样做。

useEffect(()=>{ myFunc(); } )

@robthedev 伟大的解决方案。
我们甚至可以简化代码 如果我们使用 redux:

const dispatch = useDispatch();

// redux can guarantee that the dispatch function will not change between renders.
// so we don't need to wrap it with useCallback for now.
useEffect(() => {
  dispatch(actions());
}, [dispatch]);

丹的这篇文章对我帮助很大。

这个问题的解决方案不是删除依赖项,相反,我们可以在组件外部提升不需要 props 或 state 的函数,或者将它们包装到 useCallback 定义它们的地方。

@kennylbj

在尝试了一系列不同的建议之后,这最终对我有用。 我更喜欢不使用 eslint-disable,同时尝试让代码对新手来说相对简单。 这种方法允许您传递调度函数而无需在 useCallback 内部调用它。 希望这可以帮助。

// ... inside function component
const { dispatchFunc } = customHook();
const memoizeDispatchFunc = useCallback(dispatchFunc, []);

useEffect(() => {
  memoizeDispatchFunc();
}, [memoizeDispatchFunc]);

对我来说,上面的例子不起作用。

得到这个错误:
TypeError: Cannot destructure property 'dispatchFunc' of 'componentDidMount(...)' as it is undefined.

@react-team:快乐样板代码。 :) 能否再次具有逻辑性和可读性...事件应该易于使用...无需编写 5 行以上的逻辑来调用函数。

对我来说,错误出现在以下情况:

  useEffect(() => {
    props.getTasks()
  }, [])

我是这样更正的:

const { getTasks } = props
  useEffect(() => {
    getTasks()
  }, [getTasks])

当成千上万的开发人员面临同样的问题并且 React 开发团队关闭线程并忽略每个人时:
8d6

我的问题与此线程中的其他问题不同,所以我会分享它,以防任何其他可怜的灵魂掉进兔子洞,我已经掉了 1 1/2 小时

function hydrate( _state, _errors=false) {
    console.log('hydrate()');
    setState({...state,..._state});
    if(_errors){
        setErrors({...errors,..._errors});
    }
}

用户应该将它与useEffect() ,以便像这样滋润他们的状态:

useEffect(()=>{
    hydrate({
        name:'Garrett',
        email:'[email protected]',
        newsletter: 'yes'
    });
},[hydrate]); //unfortunately, this causes an infinite render, because hydrate will always change

所以为了解决这个问题,我只是将我的水合物函数更改为包裹在useCallback() ,现在它不会在每次渲染时都发生变异(我猜),并且据说这对性能更好,如果这变成真的,我将为从我的自定义钩子返回的所有辅助函数实现这一点。

const hydrate = useCallback(( _state, _errors=false )=> {
    console.log('hydrate()');
    setState({...state,..._state});
    if(_errors){
        setErrors({...errors,..._errors});
    }
},[]);

任何人都可以确认这是否属实, useCallback正在阻止hydrate在渲染中发生变异,因此最好将所有此类函数包装在useCallback以获得更好的性能?

redux can guarantee that the dispatch function will not change between renders.

你确定吗?
请链接到它所说的文档。

检查https://overreacted.io/a-complete-guide-to-useeffect/#decoupling -updates-from-actions 了解更多细节,或者您可以检查useEffect 源代码了解实现细节。

这里我只参考丹所说的:

答案是 React 保证 dispatch 函数在整个组件生命周期中保持不变。 所以上面的例子不需要重新订阅间隔。

保证调度函数是恒定的

酷,谢谢!

当成千上万的开发人员面临同样的问题并且 React 开发团队关闭线程并忽略每个人时:
8d6

差不多这个。 在这里,我尝试使用钩子,我收到了这个毫无意义的警告,并且没有包含在任何官方文档中。 当像“请只运行这个效果一次”这样的基本用例给这么多麻烦时,真的让你怀疑钩子是否准备好进行严肃的工作。

好吧,除了线程已关闭 smh 之外,没什么可说的。

面临同样的问题!

解决方案,
(这对我有用):

const onInit = function(){ 
    console.log('initiated', Date.now());
}

useEffect(onInit, []);

我更希望在 linting 方面解决这个问题,但是嘿...

@ra30r不错的解决方法,但想知道它是否会产生副作用? :思维:

依然没有? 😔

我有一个案例,我从 Udemy 的课程中​​学会了触发表单验证错误。

这触发了缺少的依赖项“调度”,但仍然有效:

  useEffect(() => {
    if (state.business_name.value) {
      const delay = setTimeout(() => dispatch({ type: "businessNameAfterDelay" }), 1000)
      return () => clearTimeout(delay)
    }
  }, [state.business_name.value])

使用@ra30r的例子,我把它改成这样来清除错误:

  const businessNameAfterDelay = function () {
    if (state.business_name.value) {
      const delay = setTimeout(() => dispatch({ type: "businessNameAfterDelay" }), 1000)
      return () => clearTimeout(delay)
    }
  }

  useEffect(businessNameAfterDelay, [state.business_name.value])

为什么这是关闭的?

我相信已经在某处给出了答案..大声笑..无论如何..解决方案是这样做:
useEffect(() => { dispatch(); }, [dispatch]);

希望这可以帮助!!

为什么这是关闭的?

我相信已经在某处给出了答案..大声笑..无论如何..解决方案是这样做:
useEffect(() => { dispatch(); }, [dispatch]);

希望这可以帮助!!

是的,该解决方法有效, @kennylbj 前段时间在这里说。
为什么问题被关闭? 这里也有关于它的模因...... xd

我的问题有点像这样(示例代码):

  const [userStatus, setUserStatus]: any = React.useState([]);
  const [userData, setUserData]: any = React.useState([]);

  useEffect(() => {
    setUserStatus(!userStatus);
    return () => {};
  }, [userData]);

因此,在代码中,当userData发生变化时,您想更改userStatus值,在这种情况下,如果您想检查userStatus值,则将其添加到 deps:

  const [userStatus, setUserStatus]: any = React.useState([]);
  const [userData, setUserData]: any = React.useState([]);

  useEffect(() => {
    setUserStatus(!userStatus);
    return () => {};
  }, [userData, userStatus]);

在这种情况下,这将是一个永无止境的循环

@spmsupun
您可以使用 useState 的回调样式来避免在 useEffect 的依赖项数组中引入 userStatus 变量。

 useEffect(() => {
    setUserStatus(prevState => !prevState);
    return () => {};
  }, [userData]);

这就是所谓的功能更新

解决方案-->只在最后添加调度依赖
React Hook useEffect 缺少依赖项:'dispatch'。

  useEffect(() => {
    axios.get('http://localhost:8000/api/future-registrations')
      .then((result) => {
        dispatch(initRegistrationsAction(result.data));
      });
**  }, [dispatch]);**

@kennylbj这在处理一种状态时很好,我的实际场景是使用道具。 我正在从父级发送一个道具,它更改了两次,因此 usereffect 调用了两次,但我在其中运行了一个侦听器,它将侦听两次。

@spmsupun但是我认为每当依赖项数组中的变量发生变化时(例如,当 props 中的 userId 发生变化时,侦听器也需要更改时,取消注册以前的侦听器并注册一个新的侦听器是很正常的)。 而这种行为正是 react 所期望的。

如果你仍然不想让监听器被调用两次,我认为你应该更加注意传递给它的 props 并确保它不会在渲染过程中改变。

@kennylbj好吧,我确实取消了注册,但这仍然不是一个好习惯
image
我猜我必须从监听器类处理这个,

即使套接字 id 相同,您的侦听器似乎也被调用了两次。

如果满足以下情况,我认为不会发生这种情况:

ts // call useEffect if and only if sockeId changed. useEffect(() => { const unregister = register(socketId); return () => unregister(socketId); }, [socketId])

在不同的渲染过程中,依赖项数组中的其他变量是否发生了变化并导致了这种行为?

是的 3 个依赖项,但我想出了一些不同的方法

派恩

正如警告消息中所建议的那样,您可以这样做

const initFetch = useCallback(() => {
    dispatch(fetchPosts());
  }, [dispatch]);

  useEffect(() => {
    initFetch();
  }, [initFetch]);

哇,非常感谢你🙏

🚀 下面的代码片段非常适合我

在组件确实挂载

const onInit = () => getUsers()
useEffect(onInit, [])

关于参数更改

const onInit = () => getUser(id)
useEffect(onInit, [id])

最佳解决方法:react-hooks/exhaustive-deps:“关闭”
我从来没有发现这个 eslint 功能有用。 曾经。

🚀 下面的代码片段非常适合我

在组件确实挂载

const onInit = () => getUsers()
useEffect(onInit, [])

关于参数更改

const onInit = () => getUser(id)
useEffect(onInit, [id])

不错的工作。

我认为 react 文档并没有完全涵盖@luojinghui的用例,这也与我的用例相同:

我们有多种调用给定函数的方法,即: setCenterPosition() 。 它可以通过观察状态变化(通过useEffect )、点击处理程序(直接调用)等来调用。

反应文档建议

这就是为什么通常你会想要声明其中的效果所需的函数

但通常在现实世界的例子中,我们需要像通常在类组件( this.setCenterPosition(...) )上使用实例方法一样重用函数,我开始怀疑钩子是否真的适合全功能,复杂的组件。

我实际上不确定,但也许解决方案是useCallback

const setCenterPosition = useCallback(() => {
  ...
}, [Stage, elePositionArr, setElePositionArr]);

useEffect() => {
  ...
}, [stageRef, Stage, currentIndex, setCenterPosition]);

...但这对我来说有点恶心,我希望有一种更清洁的方法。 也许@gaearon可以确认?

面对同样的问题,我可以在 useEffect // eslint-disable-next-line react-hooks/exhaustive-deps结束时使用此行禁用警告 bu 但我不想使用它

这是我的代码

`const [items, setItems] = useState([
{
名称:“测试1”,
编号:1
},
{
名称:“测试2”,
编号:2
},
{
名称:“测试3”,
编号:3
},
{
名称:“测试4”,
编号:4
},
{
名称:“测试5”,
编号:5
}
]);

useEffect(() => {
const intervalId = setInterval(() => {
setItems(shuffleArray(items));
}, 1000);
返回 () => {
clearInterval(intervalId);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

我对这个问题的解决方案是在这些情况下简单地停止使用 React Hooks。 如果你的组件需要大量的状态逻辑和生命周期事件,比如componentDidMount ,那么只需要使用类组件就可以了,省去麻烦。 类组件非常好,如果这个线程证明了什么,那就是当需要复杂的状态逻辑或生命周期事件时,React Hooks 还没有准备好完全替换它们(在大多数情况下它们也没有赋予任何优势 - 你的代码_真的_使用 React Hooks 好得多?)。

我将自己使用 React Hooks 限制为简单的,比如useState来设置布尔标志等等。 如果一个组件变得复杂到需要useEffect我会将其视为一个标志,表明类组件可能更适合。

(为清楚起见进行了编辑)。

这是我现在的解决方案:

const mounted = () => {
  dispatch(something());
}

useEffect(mounted, []);

谢谢@ra30r react/issues/15865#issuecomment-651254164

此页面是否有帮助?
0 / 5 - 0 等级