react-testing-library多次点击按钮仅渲染1张图片而非新增多张如何解决
问题原因
- 核心是React异步状态更新的闭包陷阱:你定义的
getDogPhoto异步函数会捕获函数创建时的dogPhotos状态值,连续快速点击5次时,5次调用捕获的都是初始的空数组[] - 每次执行
setDogPhotos时,都是基于空数组拼接新图片地址,5次更新的结果都是长度为1的数组,最终只会渲染最后一次更新的图片 - 测试中未等待异步请求完成就连续触发点击,进一步放大了这个问题,但核心原因是状态更新的写法缺陷。
修复方案
修改getDogPhoto中的状态更新逻辑,使用函数式更新获取最新的状态值,避免依赖闭包捕获的旧状态:
export const DogGallery = () => { const [dogPhotos, setDogPhotos] = useState([]); const getDogPhoto = async () => { const newPhoto = await fetch( "https://dog.ceo/api/breeds/image/random" ).then((res) => res.json()); // 改用函数式更新,参数prevPhotos是当前最新的状态值,不受闭包影响 setDogPhotos(prevPhotos => [...prevPhotos, newPhoto.message]); }; return ( <> <p>Dog gallery</p> <Button handleClick={getDogPhoto} /> {dogPhotos.length > 0 && dogPhotos.map((photo, i) => ( <DogPhoto source={photo} key={i} number={i} /> ))} </> ); };
测试优化建议
为了让测试更稳定,建议做以下修改:
- Mock全局
fetch方法,返回固定的测试图片地址,避免依赖外部接口造成测试波动 - 可以选择每次点击后等待对应图片渲染完成,再触发下一次点击,也可以直接用
waitFor等待5张图片全部渲染:
it("should render 5 images", async () => { render(<DogGallery />); // 连续触发5次点击 for(let i=0; i<5; i++){ fireEvent.click(screen.getByText("click!")); } // 等待最多3秒直到渲染出5张图片 const elements = await waitFor( () => screen.findAllByRole("image"), { timeout: 3000 } ); expect(elements.length).toBe(5); });
内容的提问来源于stack exchange,提问作者crevulus
相关产品推荐
相关产品推荐

