我正在编写集成测试。此文件呈现 <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
已经运行,并且执行了一个副作用,即重定向到结果页面。
我知道我误解了一些东西,只是不确定是什么了。感谢您的帮助!
答案 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;