簡體   English   中英

為什么我的組件在作為孩子傳入時沒有重新渲染?

[英]Why is my component not re-rendering when passed in as a child?

我正在測試一些代碼來計算重新渲染。

這個不起作用,因為我將<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);
    });
  })

但是,這可行,但我將<MyComponent />嵌入到組件中。

  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);
    });
  })

由於MyComponent被確定為純功能組件並且它沒有 state 可言,我認為 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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM