如何测试嵌套的React Server Components?孤立测试有效但嵌套报错
解决方案:递归解析异步Server Component树
问题根源
单独测试UserData时,await UserData()直接拿到了组件执行后的最终JSX,渲染自然正常。但测试Page时,await Page()仅完成了Page自身的执行,返回的JSX里的<UserData />只是一个未触发执行的React组件元素——测试环境下的React不会自动处理嵌套的异步Server Component,渲染时会把UserData返回的Promise当作React子元素,从而抛出"Objects are not valid as a React child"错误。
实现方案
写一个递归函数,遍历组件树中的所有元素,自动执行并await所有异步组件的结果,把整个组件树解析为纯同步JSX后再渲染。
1. 编写异步组件解析工具函数
import React from 'react'; async function resolveAsyncComponents(element) { // 处理异步组件函数 if (typeof element.type === 'function' && element.type.constructor.name === 'AsyncFunction') { const resolvedElement = await element.type(element.props); return resolveAsyncComponents(resolvedElement); } // 处理React元素,递归解析子元素 if (React.isValidElement(element)) { const resolvedChildren = await Promise.all( React.Children.map(element.props.children, resolveAsyncComponents) ); return React.cloneElement(element, element.props, resolvedChildren); } // 处理数组类型的子元素 if (Array.isArray(element)) { return Promise.all(element.map(resolveAsyncComponents)); } // 普通值直接返回 return element; }
2. 修改测试代码
用上述工具函数解析Page组件后再渲染:
it('renders a heading', async () => { // 递归解析所有异步组件,得到纯同步JSX const resolvedJSX = await resolveAsyncComponents(<Page />); render(resolvedJSX); const heading = screen.getByRole('heading', { level: 1 }); expect(heading).toBeInTheDocument(); });
原理说明
这个工具函数会递归遍历整个组件树:
- 识别并执行所有异步组件函数,await其返回的JSX
- 对每个React元素,递归解析它的所有子元素
- 最终返回完全解析后的纯同步JSX结构,此时再传入
render就不会出现Promise作为子元素的问题
内容的提问来源于stack exchange,提问作者Faust
相关产品推荐
相关产品推荐

