[英]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.