期间对卸载的组件进行反应状态更新

期间对卸载的组件进行反应状态更新

本文介绍了无法摆脱警告:在测试 RTL 期间对卸载的组件进行反应状态更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

警告:无法对卸载的组件执行 React 状态更新.这是一个空操作,但它表明您的应用程序中存在内存泄漏.

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

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

describe('结果页面', () => {let goBackBtn, nearBtn;beforeEach(async() => {ZIP_API_MOCK();FDIC_API_MOCK();渲染();const zipInput = await screen.findByPlaceholderText(searchFormText.placeholder);const submitBtn = screen.getByText(searchFormText.submitBtn).closest('button');输入(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', () => {期望(真).toBeTruthy();});

抛出警告的行:

 44 |}45 |if (newResults.fiList.length > 0) fwdToPath = PATHS.RESULTS;>46 |设置结果(新结果);|^47 |设置加载(假);48 |};

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

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

解决方案

考虑使用 'mounted' 变量,来判断是否应该跳过对 setState 的调用(cleanup function).

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

 导入./App.css";import React, { useEffect, useState, useRef } from "react";import { getList, setItem } from "././services/list";功能应用(){const [alert, setAlert] = useState(false);const [list, setList] = useState([]);const [itemInput, setItemInput] = useState("");const 安装 = useRef(true);useEffect(() => {mount.current = true;如果(list.length && !alert){返回;}getList().then((items) => {如果(安装.当前){设置列表(项目);}});返回 () =>(mounted.current = false);}, [警报, 列表]);const handleSubmit = (e) =>{e.preventDefault();设置项目(项目输入)//当setItem承诺解决时,清除输入并设置警报消息.then(() => {如果(安装.当前){setItemInput("");设置警报(真);}});};useEffect(() => {如果(警报){setTimeout(() => {如果(安装.当前){设置警报(假);}}, 1000);}}, [警报]);返回 (<div><h1>我的列表</h1><ul>{list.map((item) => (<li key={item.item}>{item.item}</li>))}{警报&&<h2>提交成功}<form onSubmit={handleSubmit}><标签><p>新项目</p>setItemInput(event.target.value)}值={itemInput}/><button type="submit">Submit</button></表单>

);}导出默认应用程序;

I'm writing integration tests. This file renders the <App />, fills an input, and submits a search before each test. The tests all pass. However, I cannot get rid of:

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.

I don't get this warning in the app, only tests. I've read dozens of posts but none of the solutions (act(), wait(), waitFor(), etc...) make any difference.

Here is the test code, I've left the bare minimum of what causes the warning (where I grab goBackBtnText after clicking the 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();
  });

The line where the warning is thrown:

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

And this is what really confuses me. Because goBackBtn is on the results page and the test successfully sees it. So setResults has already been run and a side effect has been executed which is to redirect to the results page.

I'm aware I'm misunderstanding something, just not sure what anymore. Appreciate any help!

解决方案

Consider using 'mounted' variable, to tell whether it should skip the call to setState or not (cleanup function).

Check the example below for a Grocery List taken from internet:

 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;

这篇关于无法摆脱警告:在测试 RTL 期间对卸载的组件进行反应状态更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-01 02:28