我有一个应用程序,我只使用 createElement 方法渲染 React 组件,如下所示:
public static render<T>(target: HTMLElement, reactComponent: FC<T>, attrs: T) {
const root = createRoot(target);
root.render(
React.createElement(reactComponent, attrs),
);
}
代码在浏览器中运行良好,但是一旦我测试整个页面(包括组件),该组件就不会呈现。 FC 代码仅在测试结束时调用,一次性执行所有操作。
我正在使用 Karma/Jasmine 和 ReactJS 18(具有本机自动批处理功能)测试我的应用程序,直到测试结束我的组件才呈现。
上面的代码已被修改为使用flushSync()来使其立即渲染,但是flushSync还需要在我的任何钩子上完成,这感觉有点矫枉过正。
public static render<T>(target: HTMLElement, reactComponent: FC<T>, attrs: T) {
const root = createRoot(target);
// Any state change needs to be sync for Karma test purposes.
// In reality, we should not need it.
// React 18 now batches changes, which Karma does not like.
flushSync(() => root.render(
React.createElement(reactComponent, attrs),
));
}
有没有办法在集成测试(业力)中强制渲染,而不需要更改我的所有代码以使用flushSync?
谢谢
所以我找到了解决问题的方法。
我创建了一个类如下,并在断言之前调用这个flush方法:
import { act } from "react-dom/test-utils";
export default class ReactJsActions {
public static async flush() {
await act(async () => {
// tslint:disable-next-line: no-unused-expression
await new Promise((resolve) => setTimeout(resolve));
});
}
}
这是我如何使用它的示例:
it("stuff", async () => {
[code that would cause the state of the react component to change]
await ReactJsActions.flush();
expect([something]).toEqual([something]);
});
act 用于为断言准备组件,通常,在使用常用框架的reactjs 测试中,您会将正在执行的操作放在
act
lambda 方法中,然后在其后进行断言。就我而言,我正在执行一个reactjs无法控制的操作,因此将操作放入其中没有任何作用。
将
await new Promise((resolve) => setTimeout(resolve))
纳入该法案即可解决问题。
根据我的理解,带有
setTimeout(resolve)
且没有时间的新 Promise 将允许执行任何其他 setTimeout,我相信 scheduler
严重依赖于此,并且调度程序将用于批处理。
如果有人有更相关的原因,请分享。