为什么在与 fetch api 一起使用时,react hook 会抛出 act 错误?
Why does react hook throw the act error when used with fetch api?
每当我发出 API 请求并更新状态时,我的测试套件中都会出现 Warning: An update to App inside a test was not wrapped in act(...).
。
我正在使用 react-testing-library。我也尝试使用 ReactDOM 测试工具,得到了相同的结果。我尝试的另一件事是将容器包装在 act
中,仍然得到相同的结果。
请注意:我的应用程序可以运行并且我的测试通过了。我只需要知道我做错了什么,或者它是否是导致该错误出现的 react-dom 包中的错误。模拟控制台错误并将其静音是不好的。
global.fetch = require('jest-fetch-mock');
it('should clear select content item', async () => {
fetch.mockResponseOnce(JSON.stringify({ results: data }));
const { container } = render(<App />);
const content = container.querySelector('.content');
await wait();
expect(content.querySelectorAll('.content--item').length).toBe(2);
});
这是挂钩实现:
const [data, setData] = useState([]);
const [error, setError] = useState('');
const fetchInitData = async () => {
try {
const res = await fetch(API_URL);
const data = await res.json();
if (data.fault) {
setError('Rate limit Exceeded');
} else {
setData(data.results);
}
} catch(e) {
setError(e.message);
}
};
useEffect(() => {
fetchInitData();
}, [isEqual(data)]);
这是一个已知问题,请在 Github https://github.com/kentcdodds/react-testing-library/issues/281
中查看此问题
要消除 act()
警告,您需要确保您的承诺同步解决。您可以阅读 here 如何执行此操作。
总结:
The solution for this is a bit involved:
- we polyfill Promise globally with an implementation that can resolve
promises 'immediately', such as promise
- transpile your javascript with a custom babel setup like the one in this repo
- use jest.runAllTimers(); this will also now flush the promise task queue
我遇到了这个问题,放弃了使用 wait 和 async 而是使用了 jest faketimers 等等,所以你的代码应该是这样的。
global.fetch = require('jest-fetch-mock');
it('should clear select content item', /*async */ () => {
jest.useFakeTimers();
fetch.mockResponseOnce(JSON.stringify({ results: data }));
const { container } = render(<App />);
const content = container.querySelector('.content');
// await wait();
act(() => {
jest.runAllTimers();
});
expect(content.querySelectorAll('.content--item').length).toBe(2);
});
对于像我一样一年多后偶然发现这个问题的人来说,Giorgio 提到的问题已经解决,wait
已被 waitFor
取代,如此处记录:
https://testing-library.com/docs/dom-testing-library/api-async/
既然如此,我认为现在解决警告的方法应该是这样的:
import { render, waitFor } from '@testing-library/react';
// ...
it('should clear select content item', async () => {
fetch.mockResponseOnce(JSON.stringify({ results: data }));
const { container } = render(<App />);
const content = container.querySelector('.content');
await waitFor(() =>
expect(content.querySelectorAll('.content--item').length).toBe(2);
);
});
在我的例子中,我有一个 App
组件在 useEffect
挂钩中异步加载数据,因此我在每次测试时都收到此警告,使用 beforeEach
来呈现App
。这是针对我的案例的具体解决方案:
beforeEach(async () => {
await waitFor(() => render(<App />));
});
每当我发出 API 请求并更新状态时,我的测试套件中都会出现 Warning: An update to App inside a test was not wrapped in act(...).
。
我正在使用 react-testing-library。我也尝试使用 ReactDOM 测试工具,得到了相同的结果。我尝试的另一件事是将容器包装在 act
中,仍然得到相同的结果。
请注意:我的应用程序可以运行并且我的测试通过了。我只需要知道我做错了什么,或者它是否是导致该错误出现的 react-dom 包中的错误。模拟控制台错误并将其静音是不好的。
global.fetch = require('jest-fetch-mock');
it('should clear select content item', async () => {
fetch.mockResponseOnce(JSON.stringify({ results: data }));
const { container } = render(<App />);
const content = container.querySelector('.content');
await wait();
expect(content.querySelectorAll('.content--item').length).toBe(2);
});
这是挂钩实现:
const [data, setData] = useState([]);
const [error, setError] = useState('');
const fetchInitData = async () => {
try {
const res = await fetch(API_URL);
const data = await res.json();
if (data.fault) {
setError('Rate limit Exceeded');
} else {
setData(data.results);
}
} catch(e) {
setError(e.message);
}
};
useEffect(() => {
fetchInitData();
}, [isEqual(data)]);
这是一个已知问题,请在 Github https://github.com/kentcdodds/react-testing-library/issues/281
中查看此问题要消除 act()
警告,您需要确保您的承诺同步解决。您可以阅读 here 如何执行此操作。
总结:
The solution for this is a bit involved:
- we polyfill Promise globally with an implementation that can resolve promises 'immediately', such as promise
- transpile your javascript with a custom babel setup like the one in this repo
- use jest.runAllTimers(); this will also now flush the promise task queue
我遇到了这个问题,放弃了使用 wait 和 async 而是使用了 jest faketimers 等等,所以你的代码应该是这样的。
global.fetch = require('jest-fetch-mock');
it('should clear select content item', /*async */ () => {
jest.useFakeTimers();
fetch.mockResponseOnce(JSON.stringify({ results: data }));
const { container } = render(<App />);
const content = container.querySelector('.content');
// await wait();
act(() => {
jest.runAllTimers();
});
expect(content.querySelectorAll('.content--item').length).toBe(2);
});
对于像我一样一年多后偶然发现这个问题的人来说,Giorgio 提到的问题已经解决,wait
已被 waitFor
取代,如此处记录:
https://testing-library.com/docs/dom-testing-library/api-async/
既然如此,我认为现在解决警告的方法应该是这样的:
import { render, waitFor } from '@testing-library/react';
// ...
it('should clear select content item', async () => {
fetch.mockResponseOnce(JSON.stringify({ results: data }));
const { container } = render(<App />);
const content = container.querySelector('.content');
await waitFor(() =>
expect(content.querySelectorAll('.content--item').length).toBe(2);
);
});
在我的例子中,我有一个 App
组件在 useEffect
挂钩中异步加载数据,因此我在每次测试时都收到此警告,使用 beforeEach
来呈现App
。这是针对我的案例的具体解决方案:
beforeEach(async () => {
await waitFor(() => render(<App />));
});