Reactjs 使用对话框中的有状态按钮或材质UI中的警报来响应内存泄漏警告
我正在为组件库使用材质UI,并注意到当我单击对话框或警报(两个组件都管理打开/关闭状态)中的按钮时,会收到内存泄漏警告。我不确定如何解决这个问题。按钮组件在单击时使用状态创建活动类,它使用Reactjs 使用对话框中的有状态按钮或材质UI中的警报来响应内存泄漏警告,reactjs,material-ui,state,react-hooks,jss,Reactjs,Material Ui,State,React Hooks,Jss,我正在为组件库使用材质UI,并注意到当我单击对话框或警报(两个组件都管理打开/关闭状态)中的按钮时,会收到内存泄漏警告。我不确定如何解决这个问题。按钮组件在单击时使用状态创建活动类,它使用setTimeoutonClick使按钮单击在UI中更可见/更持久 这是按钮组件: function Button({ classes, className, onClick, ...props }) { let [active, setActive] = useState(false);
setTimeout
onClick使按钮单击在UI中更可见/更持久
这是按钮组件:
function Button({
classes,
className,
onClick,
...props
}) {
let [active, setActive] = useState(false);
let handleClick = e => {
e.persist();
setActive(true);
setTimeout(() => {
setActive(false);
}, 250);
if (typeof onClick === "function") onClick(e);
};
return (
<MuiButton
variant={finalVariant(variant)}
className={`${active ? "Mui-active" : ""} ${className}`}
classes={buttonClasses}
onClick={handleClick}
{...props}
/>
);
}
let containedStyle = color => ({
"&:active": {
backgroundColor: color.dark
},
"&.Mui-active": {
backgroundColor: color.dark
}
});
index.js:1437 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.
我已尝试使用警告提示的
useffect
清除活动状态,但没有成功。下面是一个演示,演示了当我使用一个用MUI构建的自定义按钮时会发生什么情况,该按钮在对话框或警报中使用时使用挂钩来管理状态,这是因为您的handleClick
函数使用setTimeout
:
let handleClick = e => {
e.persist();
setActive(true);
setTimeout(() => {
setActive(false);
}, 250);
if (typeof onClick === "function") onClick(e);
};
以更新状态
调用onClick
时,父组件正在卸载该组件,但仍有一个订阅(您的超时)保持活动状态
如果这是一次过的活动,比如在本例中,这其实没什么大不了的。这是警告,不是错误。此警告的主要目的是让您知道在卸载某些内容后,您是否将订阅或引用保留很长时间
有一些解决方法可以通过在组件卸载时设置标志来消除警告,如果设置了标志,则不更新状态,但这并不能真正解决卸载后保留对组件的引用的问题
解决此问题的更好方法是使用React.useRef()
保留对超时的引用,然后在useffect()
中清除它,如下所示:
功能按钮({
班级,
类名,
onClick,
…道具
}) {
let[active,setActive]=useState(false);
+const timeout=React.useRef(未定义);
+React.useffect(()=>{
+return()=>{
+if(timeout.current!==未定义){
+clearTimeout(timeout.current);
+ }
+ }
+ }, []);
让handleClick=e=>{
e、 坚持();
setActive(真);
-设置超时(()=>{
+timeout.current=setTimeout(()=>{
setActive(假);
}, 250);
if(typeof onClick==“function”)onClick(e);
};
返回(
);
}
可以将其封装在钩子中,如下所示:
function useSafeTimeout() {
const timeouts = React.useRef([])
React.useEffect(() => {
return () => {
timeouts.forEach(timeout => {
clearTimeout(timeout)
})
}
}, [])
return React.useCallback((fn, ms, ...args) => {
const cancel = setTimeout(fn, ms, ...args)
timeouts.current.push(cancel)
}, [])
}
并以这种方式使用:
功能按钮({
班级,
类名,
onClick,
…道具
}) {
let[active,setActive]=useState(false);
+const setTimeout=useSafetTimeout();
让handleClick=e=>{
e、 坚持();
setActive(真);
设置超时(()=>{
setActive(假);
}, 250);
if(typeof onClick==“function”)onClick(e);
};
返回(
);
}
以下是我的问题解决方案:
function Button({
classes,
className,
onClick,
...props
}) {
let [active, setActive] = useState(false);
let timeoutIds = useRef([]);
let registerTimeout = (f, ms) => {
let timeoutId = setTimeout(f, ms);
timeoutIds.current.push(timeoutId);
};
let handleClick = e => {
e.persist();
setActive(true);
if (typeof onClick === "function") onClick(e);
};
let cleanup = () => {
timeoutIds.current.forEach(clearTimeout);
};
useEffect(() => {
if (active === true) {
registerTimeout(() => setActive(false), 250);
}
return cleanup;
}, [active]);
return (
<MuiButton
variant={finalVariant(variant)}
className={`${active ? "Mui-active" : ""} ${className}`}
classes={buttonClasses}
onClick={handleClick}
{...props}
/>
);
}
功能按钮({
班级,
类名,
onClick,
…道具
}) {
let[active,setActive]=useState(false);
让timeoutIds=useRef([]);
let registerTimeout=(f,ms)=>{
let timeoutId=setTimeout(f,ms);
timeoutId.current.push(timeoutId);
};
让handleClick=e=>{
e、 坚持();
setActive(真);
if(typeof onClick==“function”)onClick(e);
};
让清理=()=>{
timeoutIds.current.forEach(clearTimeout);
};
useffect(()=>{
如果(活动===真){
registerTimeout(()=>setActive(false),250);
}
回流清理;
},[活跃];
返回(
);
}
stackblitz.com/fork/react你能在这里添加你的代码吗,这样我就可以有一个清晰的视图这里是一个警告的演示;嘿,丹!谢谢你的回答:)上面添加到代码演示中的代码片段确实清除了内存泄漏警告(哇!)但它似乎没有按照预期在超时后清除活动状态/类。我在此处更新:*我已使活动类在按钮上设置了红色背景色,以明确在单击按钮->查看状态更改的整个过程中发生了什么。如果我遗漏了一些明显的内容,请道歉!!这是版本n没有使用useffect+useRef进行比较:哦,糟糕,我犯了一个错误。修复了它!每次重新呈现组件时,超时都会被删除,而不仅仅是在卸载组件时。感谢您回复我/修改!:D!我还尝试添加更改,并且获得了超过最大调用堆栈大小的值:`var cancel=setTimeout.apply(未定义,[fn,ms].concat(args));`。这是通过实现useSafetTimeout
函数,并通过要在handleClick中调用的函数设置此const setTimeout
。有什么想法吗?我会继续探索,但这是当前的状态,这里有修订