[英]Still getting "React state update on an unmounted component" error with React Testing Library tests
I was getting a plethora of errors with my testing code so have taken several steps to improve things.我的测试代码出现大量错误,因此采取了几个步骤来改进。 This article helped: https://binarapps.com/blog/clean-up-request-in-useeffect-react-hook/woodb
这篇文章有帮助: https : //binarapps.com/blog/clean-up-request-in-useeffect-react-hook/woodb
I improved the useEffect
that I suspected was the problem by introducing AbortController:我提高了
useEffect
,我怀疑是通过引入AbortController问题:
useEffect(() => {
if (companyId && companyId !== -1) {
const abortController = new AbortController();
requests.get(`${requests.API_ROOT()}account_management/roles?company_id=${companyId}`)
.then(response => {
dispatch({type: UPDATE_ROLES, payload: response.data.roles});
})
.catch(error => {
if (error.response) {
throw('Error fetching roles: ', error.response.status);
}
});
return () => {
abortController.abort();
};
}
}, [companyId, dispatch]);
I also refactored my test code like this:我还像这样重构了我的测试代码:
it('Should hide modal if Close is pressed', async () => {
await act(async () => {
let { getByTestId, getByText, queryByTestId, queryByText } = await renderDom(<AddUsersLauncher />);
fireEvent.click(queryByText(/^Add Users/i));
fireEvent.click(getByText(/^Close/i));
expect(queryByTestId('add-users-modal')).toBeNull();
});
});
Note: The renderDom
function remains the same as before:注意:
renderDom
函数和以前一样:
const _initialState = {
session: {
user: {},
isLoading: false,
error: false,
},
};
function renderDom(component, initialState = _initialState) {
const store = configureStore(initialState);
return {
...render(
<Provider store={store}>
<SessionProvider>
<ThemeProvider theme={theme}>
<SystemMessages />
<CustomComponent />
{component}
</ThemeProvider>
</SessionProvider>
</Provider>),
store
};
}
As per the question from oemera, this is the async call:根据 oemera 的问题,这是异步调用:
export const get = async function(url: string, _options: any) {
const options = _options || await _default_options();
return axios.get(url, options);
};
After this refactoring, the errors/warnings have reduced but there's still one appearing:经过这次重构,错误/警告减少了,但仍然出现:
> Add Users component tests
> ✓ Should display the Add Users modal (119ms)
> ✓ Should cancel w/o confirmation modal if no data is entered (34ms)
> ✓ Should hide modal if Close is pressed (32ms)
> ✓ Should hide modal if Close is pressed (29ms)
>
> console.error
> node_modules/react-dom/cjs/react-dom.development.js:558
> Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your
> application. To fix, cancel all subscriptions and asynchronous tasks
> in a useEffect cleanup function.
> in UsersProvider (at AddUsersModal.js:10)
> in div (at AddUsersModal.js:9)
> in AddUsersModal (at AddUsersLauncher.js:16)
>
> Test Suites: 1 passed, 1 total Tests: 4 passed, 4 total
> Snapshots: 0 total Time: 4.999s Ran all test suites matching
> /AddUsers.test/i.
I've checked all of the code AddUsers...
files mentioned but there are no useEffect
instances there.我已经检查了所有提到的代码
AddUsers...
文件,但那里没有useEffect
实例。
Any advice on what I should do to remove this warning?关于我应该怎么做才能删除此警告的任何建议?
When you are using axios
instead of fetch
the abort controller won't work.当您使用
axios
而不是fetch
,中止控制器将不起作用。 This is how you do it in axios:这是你在 axios 中的做法:
import React, { Component } from 'react';
import axios from 'axios';
class Example extends Component {
signal = axios.CancelToken.source();
state = {
isLoading: false,
user: {},
}
componentDidMount() {
this.onLoadUser();
}
componentWillUnmount() {
this.signal.cancel('Api is being canceled');
}
onLoadUser = async () => {
try {
this.setState({ isLoading: true });
const response = await axios.get('https://randomuser.me/api/', {
cancelToken: this.signal.token,
})
this.setState({ user: response.data, isLoading: true });
} catch (err) {
if (axios.isCancel(err)) {
console.log('Error: ', err.message); // => prints: Api is being canceled
}
else {
this.setState({ isLoading: false });
}
}
}
render() {
return (
<div>
<pre>{JSON.stringify(this.state.user, null, 2)}</pre>
</div>
)
}
}
source: https://gist.github.com/adeelibr/d8f3f8859e2929f3f1adb80992f1dc09来源: https : //gist.github.com/adeelibr/d8f3f8859e2929f3f1adb80992f1dc09
Note how you pass in a cancelToken
instead of the whole signal.请注意您如何传入
cancelToken
而不是整个信号。 Also you get the signal with axios.CancelToken.source()
and you call signal.cancel
instead or abortController.abort
你也获得与信号
axios.CancelToken.source()
和调用signal.cancel
代替或abortController.abort
So for your example, this should work like this or at least lead you in the right direction:因此,对于您的示例,这应该像这样工作,或者至少将您引向正确的方向:
useEffect(() => {
if (companyId && companyId !== -1) {
const signal = axios.CancelToken.source();
requests.get(`${requests.API_ROOT()}account_management/roles?company_id=${companyId}`,
{ cancelToken: signal.token})
.then(response => {
dispatch({type: UPDATE_ROLES,
payload: response.data.roles}); })
.catch(error => {
if (error.response) {
throw('Error fetching roles: ' error.response.status);
}
});
return () => { signal.cancel('Cancelling request...');};
} }, [companyId, dispatch]);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.