无法摆脱警告:在测试 RTL 期间对卸载的组件进行反应状态更新

时间:2021-06-02 19:09:03

标签: reactjs jestjs react-hooks react-testing-library

我正在编写集成测试。此文件呈现 <App />,填充输入,并在每次测试之前提交搜索。测试全部通过。但是,我无法摆脱:

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.

我在应用程序中没有收到此警告,只有测试。我已经阅读了数十篇帖子,但没有一个解决方案(act()wait()waitFor() 等)有任何区别。

这是测试代码,我保留了导致警告的最低限度(点击 goBackBtnText 后我抓住了 submitBtn

describe('Results Page', () => {
  let goBackBtn, nearbyBtn;
  beforeEach(async () => {
    ZIP_API_MOCK();
    FDIC_API_MOCK();
    render(<App />);

    const zipInput = await screen.findByPlaceholderText(searchFormText.placeholder);
    const submitBtn = screen.getByText(searchFormText.submitBtn).closest('button');
    input(zipInput, VALID_ZIP_WITH_RESULTS);
    userEvent.click(submitBtn);
    const goBackBtnText = await screen.findByText((content) =>
      content.includes(resultsText.goBackBtn)
    );
    goBackBtn = goBackBtnText.closest('button');
  });

  afterEach(() => {
    userEvent.click(goBackBtn);
  });

  it('true', () => {
    expect(true).toBeTruthy();
  });

抛出警告的行:

 44 |     }
      45 |     if (newResults.fiList.length > 0) fwdToPath = PATHS.RESULTS;
    > 46 |     setResults(newResults);
         |     ^
      47 |     setLoading(false);
      48 |   };

这才是真正让我困惑的地方。因为 goBackBtn 在结果页上,测试成功看到了。因此 setResults 已经运行,并且执行了一个副作用,即重定向到结果页面。

我知道我误解了一些东西,只是不确定是什么了。感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

考虑使用 'mounted' 变量来判断是否应该跳过对 setState 的调用(清理函数)。

检查下面的示例以获取从互联网上获取的杂货清单:

 import "./App.css";
    import React, { useEffect, useState, useRef } from "react";
    import { getList, setItem } from "././services/list";
    
    function App() {
      const [alert, setAlert] = useState(false);
      const [list, setList] = useState([]);
      const [itemInput, setItemInput] = useState("");
      const mounted = useRef(true);
    
      useEffect(() => {
        mounted.current = true;
        if (list.length && !alert) {
          return;
        }
        getList().then((items) => {
          if (mounted.current) {
            setList(items);
          }
        });
        return () => (mounted.current = false);
      }, [alert, list]);
    
      const handleSubmit = (e) => {
        e.preventDefault();
        setItem(itemInput)
          //When the setItem promise resolves, clear the input and set the alert message
          .then(() => {
            if (mounted.current) {
              setItemInput("");
              setAlert(true);
            }
          });
      };
    
      useEffect(() => {
        if (alert) {
          setTimeout(() => {
            if (mounted.current) {
              setAlert(false);
            }
          }, 1000);
        }
      }, [alert]);
    
      return (
        <div>
          <h1>My List</h1>
          <ul>
            {list.map((item) => (
              <li key={item.item}>{item.item}</li>
            ))}
          </ul>
          {alert && <h2> Submit Successful</h2>}
          <form onSubmit={handleSubmit}>
            <label>
              <p>New Item</p>
              <input
                type="text" 
                onChange={(event) => setItemInput(event.target.value)}
                value={itemInput}
              />
            </label>
            <button type="submit">Submit</button>
          </form>
        </div>
      );
    }
    
    export default App;