为什么使用useContext useEffect和setInterval的此自定义React钩子在测试中失败

时间:2019-08-24 20:45:32

标签: reactjs react-hooks react-testing-library

我正在尝试测试自定义的React钩子。

我不明白为什么setInterval内部的回调函数运行时为什么不使用新的上下文。

@ testing-library / react不会出现问题,因为它可以使用新的上下文重新渲染。很有可能在useContext,useEffect和setInterval之间发生了某些事情,但是我不知道该怎么办。

自定义React钩子'useCustomContext.ts':

import { useContext, useEffect, useRef, createContext } from 'react';

export const CustomContext = createContext('');

export const useValueFromContext = function() {
  const context = useContext(CustomContext);
  const ref = useRef('');

  function getContext() {
    return context;
  }

  useEffect(() => {
    ref.current = getContext();
    const interval = setInterval(() => {
      ref.current = getContext();
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  return ref.current;
};

export default useValueFromContext;

测试“ useCustomContext.test.tsx”失败:

import React from 'react';
import { useValueFromContext, CustomContext } from './useCustomContext';
import { render } from '@testing-library/react';

test('Should return value from most recently provided context', async () => {
  const Component = () => {
    const value = useValueFromContext();
    return <span data-testid="context">{value}</span>;
  };

  const { getByTestId, rerender } = render(
    <CustomContext.Provider value="a">
      <Component />
    </CustomContext.Provider>,
  );

  rerender(
    <CustomContext.Provider value="b">
      <Component />
    </CustomContext.Provider>,
  );

  await new Promise(resolve => {
    setTimeout(() => {
      rerender(
        <CustomContext.Provider value="b">
          <Component />
        </CustomContext.Provider>,
      );
      resolve();
    }, 2000);
  });

  expect(getByTestId('context').textContent).toBe('b');
});

输出:

Should return value from most recently provided context

    expect(received).toBe(expected) // Object.is equality

    Expected: "b"
    Received: "a"

1 个答案:

答案 0 :(得分:0)

我发现了问题。我必须像这样将上下文添加到useEffect依赖项:

  useEffect(() => {
    ref.current = getContext();
    const interval = setInterval(() => {
      ref.current = getContext();
    }, 1000);
    return () => clearInterval(interval);
  }, [context]);