【问题标题】:Why is my component not re-rendering when passed in as a child?为什么我的组件在作为孩子传入时没有重新渲染?
【发布时间】:2023-03-29 20:21:01
【问题描述】:

我正在测试一些代码来计算重新渲染。

这个不起作用,因为我小时候传递<MyComponent>

  it("should get the same object when the parent rerenders", async () => {
    jest.useFakeTimers();
    const callback = jest.fn();
    let renderCount = 0;
    let x = 0;
    function MyComponent() {
      const random = Math.random();
      const myRef = useRef({ random })
      if (x === 0) {
        x = myRef.current.random
      }
      ++renderCount;
      callback();
      return (<div data-testid="test">{JSON.stringify(myRef.current)}</div>);
    }

    function MyStateComponent({ children }: PropsWithChildren<{}>) {
      const forceUpdate = useReducer(() => ({}), {})[1] as () => void
      useEffect(() => {
        (async function asyncEffect() {
          await delay(10000);
          forceUpdate()
        })()
      }, [])
      return (<>{children}</>);
    }

    const { getByTestId } = render(<MyStateComponent><MyComponent /></MyStateComponent>)
    expect(getByTestId("test").textContent).toEqual(JSON.stringify({ random: x }));
    expect(renderCount).toEqual(1);
    expect(callback).toBeCalledTimes(1);
    jest.runAllTimers();
    await waitFor(() => {
      expect(callback).toBeCalledTimes(2);
      expect(getByTestId("test").textContent).toEqual(JSON.stringify({ random: x }));
      expect(renderCount).toEqual(2);
    });
  })

但是,这可行,但我将 &lt;MyComponent /&gt; 嵌入到组件中。

  it("should get the same object when the parent rerenders with children", async () => {
    jest.useFakeTimers();
    const callback = jest.fn();
    let renderCount = 0;
    let x = 0;
    function MyComponent() {
      const random = Math.random();
      const myRef = useRef({ random })
      if (x === 0) {
        x = myRef.current.random
      }
      ++renderCount;
      callback();
      return (<div data-testid="test">{JSON.stringify(myRef.current)}</div>);
    }

    function MyStateComponent({ children }: PropsWithChildren<{}>) {
      const forceUpdate = useReducer(() => ({}), {})[1] as () => void
      useEffect(() => {
        (async function asyncEffect() {
          await delay(10000);
          forceUpdate()
        })()
      }, [])
      return (<MyComponent />);
    }

    const { getByTestId } = render(<MyStateComponent />)
    expect(getByTestId("test").textContent).toEqual(JSON.stringify({ random: x }));
    expect(renderCount).toEqual(1);
    expect(callback).toBeCalledTimes(1);
    jest.runAllTimers();
    await waitFor(() => {
      expect(callback).toBeCalledTimes(2);
      expect(getByTestId("test").textContent).toEqual(JSON.stringify({ random: x }));
      expect(renderCount).toEqual(2);
    });
  })

【问题讨论】:

    标签: javascript reactjs typescript jestjs


    【解决方案1】:

    由于MyComponent 被确定为纯函数组件并且它没有状态可言,我认为 React 会自动记忆它。为了解决这个问题并强制重新渲染,组件需要改变自身的一部分,例如一个上下文。

    import { createContext, PropsWithChildren, useContext, useEffect, useReducer } from "react";
    import { delay } from "./delay";
    
    type IRendering = {}
    const RenderingContext = createContext<IRendering>({})
    /**
     * This is a component that rerenders after a short delay
     */
    export function RerenderingProvider({ children }: PropsWithChildren<{}>): JSX.Element {
      const forceUpdate = useReducer(() => ({}), {})[1] as () => void
    
      useEffect(() => {
        (async function asyncEffect() {
          await delay(10000);
          forceUpdate();
        })()
      }, [])
      return (<RenderingContext.Provider value={{}}>{children}</RenderingContext.Provider>);
    }
    export function useRerendering(): IRendering {
      return useContext(RenderingContext);
    }
    

    通过以下测试...

      it("should get the same object when the parent rerenders using component, but the component will rerender as context has changed", async () => {
        jest.useFakeTimers();
        const callback = jest.fn();
        let x = 0;
        function MyComponent() {
          const _ignored = useRerendering();
          const random = Math.random();
          const myRef = useRef({ random })
          if (x === 0) {
            x = myRef.current.random
          }
          callback();
          return (<>
            <div data-testid="test">{JSON.stringify(myRef.current)}</div>
            <div data-testid="random">{JSON.stringify(random)}</div>
          </>);
        }
    
        const { getByTestId } = render(<RerenderingProvider><MyComponent /></RerenderingProvider>)
        expect(getByTestId("test").textContent).toEqual(JSON.stringify({ random: x }));
        expect(callback).toBeCalledTimes(1);
        jest.runAllTimers();
        await waitFor(() => {
          expect(getByTestId("test").textContent).toEqual(JSON.stringify({ random: x }));
          expect(callback).toBeCalledTimes(2);
        });
      })
    

    我在这里提出了我的方案...https://github.com/trajano/react-hooks-tests

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-02
      • 2019-02-08
      • 2021-11-24
      • 2014-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多