React TS项目出现「无法对已卸载组件执行state更新」警告怎么解决
问题根因
这个警告触发的核心原因是:你的MyModal和UpdateForm组件中存在未做取消/挂载判断的异步请求,在两种场景下会触发卸载组件的状态更新:
- 用户触发更新/删除书籍的请求后,不等接口返回就手动关闭了Modal,此时两个组件已经卸载,接口返回后执行
setError/setReload等状态更新操作就会触发警告 - 接口返回后你先执行了状态更新,再触发
window.location.reload()刷新页面,刷新过程中组件开始卸载,刚触发的状态更新就命中了规则
修复方案
方案1:最简修复(保留现有刷新逻辑)
给两个组件增加挂载状态标记,所有状态更新前先判断组件是否仍处于挂载状态:
UpdateForm.tsx 修改示例
import { useState, SyntheticEvent, useRef, useEffect } from "react"; const UpdateForm = (props: {bookId: string, setReload: (reload: boolean) => void}) => { const [bookStatus, setBookStatus] = useState('reading') const [error, setError] = useState(<></>) const bookId = props.bookId // 新增挂载标记 const isMounted = useRef(true) useEffect(() => { return () => { isMounted.current = false } }, []) const submit = async (e: SyntheticEvent) => { e.preventDefault(); if (window.confirm(`Do you want to move this book to ${bookStatus}?`)) { const response = await fetch(`${process.env.REACT_APP_API_BASE_URL}/books/updatestatus`, { method: 'POST', headers: {'Content-Type': 'application/json'}, credentials: 'include', body: JSON.stringify({ "status": bookStatus, "id": bookId }) }); const content = await response.json(); // 所有状态更新前先判断是否挂载 if (!isMounted.current) return console.log(bookStatus) if (content.status !== 200) { setError(( <div className="alert alert-danger" role="alert"> {content.message} </div> )) } else { setError(( <div className="alert alert-success" role="alert"> {content.message} </div> )) props.setReload(true) } } } // 其余渲染代码保持不变 }
MyModal.tsx 同理增加相同的isMounted逻辑即可。
方案2:更优体验修复(推荐)
你已经使用了react-query管理数据,完全可以不用window.location.reload()这种全页刷新的方案,替换为刷新react-query缓存即可,体验更好也能避免该问题:
- 移除MyModal中的reload状态和handleClose中的刷新逻辑
- 操作成功后调用
invalidateQueries刷新书籍列表:
// MyModal.tsx 中先导入依赖 import { useQueryClient } from 'react-query' const MyModal = (props: {show: boolean, book: BookType, setShow: (status: boolean) => void }) => { const queryClient = useQueryClient() const [error, setError] = useState(<></>) // 移除reload相关状态定义 const bookId = props.book.id const bookName = props.book.name const handleClose = () => { props.setShow(false); } const deleteBook = async (e: SyntheticEvent) => { e.preventDefault() if (window.confirm("Are you sure?")) { const response = await fetch(`${process.env.REACT_APP_API_BASE_URL}/books/deletebook`, { method: 'POST', headers: {'Content-Type': 'application/json'}, credentials: 'include', body: JSON.stringify({ "id": bookId, "name": bookName }) }); const content = await response.json(); if (content.status !== 200) { setError(( <div className="alert alert-danger" role="alert"> {content.message} </div> )) } else { setError(( <div className="alert alert-warning" role="alert"> {content.message} </div> )) // 刷新books缓存,列表自动更新无需刷新页面 queryClient.invalidateQueries('books') // 操作成功后延迟关闭弹窗,让用户看到成功提示 setTimeout(() => props.setShow(false), 1000) } } } // 其余渲染代码保持不变,UpdateForm同理替换setReload为invalidateQueries逻辑即可 }
内容的提问来源于stack exchange,提问作者Pratik
相关产品推荐
相关产品推荐

