React useEffect钩子不调用模拟函数

时间:2019-03-10 13:27:10

标签: reactjs jestjs react-hooks

编辑:现在,此问题已解决,但我不确定为什么。请查看这篇原始文章以及下面的其他修改内容,感谢您提供有关为什么现在可行的反馈。

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

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

在我的单元测试中使用Jest模拟了该功能:

const searchForVideos = jest.fn();

因此,根据我的理解,useEffect()应该在组件渲染后立即首次运行。我可以将console.log()语句放在useEffect()回调中,并且确实会打印到控制台。但是,此Expect()语句失败:

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

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

我应该了解一些有关使模拟功能与React挂钩一起正常工作的知识吗?

编辑:为了回应评论,我进行了更改以解决问题。不过,我不明白为什么这样做。

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

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

所以我想我只是希望有人可以向我更好地解释这些功能以及这种更改为何起作用。谢谢。

1 个答案:

答案 0 :(得分:0)

关于您的requestAnimationFrame为何要解决的第二个问题,请参见下面的文档摘录。它仅涉及useEffectuseLayoutEffect的特定时间-具体来说是“ useEffect推迟到浏览器绘制完成之后”。 requestAnimationFrame以类似的方式延迟您的测试代码。我希望如果您将代码更改为使用useLayoutEffect,则可以使用测试的原始版本(但是useEffect是适合您的案例的钩子)。

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

  

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

     

但是,并非所有效果都可以延迟。例如,DOM突变   用户可见的必须在下一个之前同步触发   绘画,以使用户不会感觉到视觉上的不一致。 (   区别在概念上类似于被动与主动事件   对于这些类型的效果,React提供了另外一种   挂钩称为useLayoutEffect。它具有与useEffect相同的签名,   并且仅在触发时有所不同。

     

尽管useEffect推迟到浏览器绘制完成之后,   可以确保在任何新渲染之前先触发。 React将永远   在开始新更新之前,请刷新以前渲染的效果。

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

  

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

     

尽可能使用标准useEffect以避免阻塞视觉效果   更新。