React Testing库测试仍然出现“未安装组件上的反应状态更新”错误

时间:2019-11-30 00:49:21

标签: reactjs jestjs react-testing-library use-effect

我的测试代码出现了很多错误,因此采取了一些措施来进行改进。本文有助于:https://binarapps.com/blog/clean-up-request-in-useeffect-react-hook/woodb

我通过引入AbortController改进了我怀疑是问题的useEffect

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]);

我还这样重构了测试代码:

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();
  });
});

注意: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
  };
}

根据oemera的问题,这是异步调用:

export const get = async function(url: string, _options: any) {
  const options = _options || await _default_options();
  return axios.get(url, options);
};

此重构后,错误/警告有所减少,但仍然出现以下情况:

>   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.

我已经检查了所有提到的代码AddUsers...文件,但是那里没有useEffect实例。

有关我应该如何删除此警告的任何建议?

1 个答案:

答案 0 :(得分:2)

当您使用axios而不是fetch时,异常中止控制器将无法工作。这是您在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>
      )
    }
}

来源: https://gist.github.com/adeelibr/d8f3f8859e2929f3f1adb80992f1dc09

请注意如何传递cancelToken而不是整个信号。另外,您可以使用axios.CancelToken.source()来获得信号,然后调用signal.cancelabortController.abort

因此,在您的示例中,这应该像这样工作或至少将您引向正确的方向:

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]);