首页 > 解决方案 > 反应useEffect钩子不调用模拟函数

问题描述

我是 React 钩子的新手。我在组件中使用了 useEffect() 钩子,该钩子从我的道具中调用了一个函数 searchForVideos():

useEffect(() => {
  props.searchForVideos();
}, [currentPage]);

该函数在我的单元测试中使用 Jest 进行了模拟:

const searchForVideos = jest.fn();

因此,根据我的理解,useEffect() 应该在组件渲染后立即运行。我可以在 useEffect() 回调中放置一个 console.log() 语句,它会打印到控制台。但是,此 expect() 语句失败:

const component = mountComponent();
setImmediate(() => {
    expect(searchForVideos).toHaveBeenCalled();
});

这很奇怪,因为我已经确认钩子正在运行,如果它正在运行,它应该调用该函数。但是,那条线总是失败。

有什么我应该知道的让模拟函数与 React 钩子一起工作吗?

更新

作为对评论的回应,我进行了更改以解决问题。不过,我不明白为什么会这样。

const component = mountComponent();
requestAnimationFrame(() => {
    expect(searchForVideos).toHaveBeenCalled();
    done();
});

所以我只是用 requestAnimationFrame() 替换了 setImmediate(),现在一切正常。我以前从未接触过 requestAnimationFrame() 。我对 setImmediate() 的理解是,它基本上会立即将回调排队在事件队列的后面,因此队列中的任何其他 JavaScript 任务都会在它之前运行。

因此,理想情况下,我正在寻求有关这些功能以及此更改为何起作用的解释。

标签: reactjsjestjsreact-hooks

解决方案


至于为什么要requestAnimationFrame修复它的次要问题,请参阅下面的文档摘录。它只是进入了特定的时间,useEffect并且useLayoutEffect——特别是“useEffect 被推迟到浏览器绘制之后”。requestAnimationFrame以类似的方式延迟您的测试代码。我希望如果您将代码更改为 use useLayoutEffect,您的测试的原始版本将起作用(但useEffect对于您的情况来说是合适的钩子)。

来自https://reactjs.org/docs/hooks-reference.html#timing-of-effects

与 componentDidMount 和 componentDidUpdate 不同,传递给 useEffect 的函数在布局和绘制之后在延迟事件期间触发。这使它适用于许多常见的副作用,例如设置订阅和事件处理程序,因为大多数类型的工作不应该阻止浏览器更新屏幕。

但是,并非所有影响都可以推迟。例如,对用户可见的 DOM 突变必须在下一次绘制之前同步触发,以便用户不会感知到视觉上的不一致。(区别在概念上类似于被动和主动事件侦听器。)对于这些类型的效果,React 提供了一个额外的 Hook,称为 useLayoutEffect。它与 useEffect 具有相同的签名,只是在触发时有所不同。

尽管 useEffect 被推迟到浏览器绘制之后,但它保证在任何新的渲染之前触发。在开始新的更新之前,React 总是会刷新先前渲染的效果。

来自https://reactjs.org/docs/hooks-reference.html#uselayouteffect

签名与 useEffect 相同,但它在所有 DOM 突变后同步触发。使用它从 DOM 中读取布局并同步重新渲染。在 useLayoutEffect 中安排的更新将在浏览器有机会绘制之前同步刷新。

尽可能首选标准 useEffect 以避免阻塞视觉更新。


推荐阅读