组件卸载后状态仍保留?React中useRef引发的状态困惑
问题解析:useRef值持续递增的原因
先看你的代码:
function Middle() { const [show, setShow]=useState(false); return ( <div> <button onClick={()=>setShow(v=>!v)}>MIDDLE</button> <Bottom visible={show}/> </div> ); } function Bottom(props) { const {visible} = props; const state = useRef(0); useEffect(() => { console.log('Hey', state.current++); }); return ( <> {visible && <div>BOTTOM</div> } </> ); }
核心问题:Bottom组件从未被卸载
你以为点击按钮时Bottom会挂载/卸载,但实际上Bottom组件自始至终都在组件树里,只是它内部的<div>BOTTOM</div>被条件渲染藏起来了而已。
- Middle组件每次渲染都会输出
<Bottom visible={show}/>,不管show是true还是false,Bottom组件本身根本没被从DOM里移除,只是子元素不显示了。 - useRef的特性是:组件挂载后,它创建的引用会一直跟着组件走,直到组件彻底卸载。既然Bottom从未卸载,那
state.current就会一直保留之前的数值,每次触发useEffect自然就会递增。
实现真正的挂载/卸载怎么改
如果想让Bottom在show为false时完全卸载,得把条件渲染放到Middle里,直接控制Bottom组件本身的渲染,而不是传个visible让Bottom内部控制子元素:
function Middle() { const [show, setShow]=useState(false); return ( <div> <button onClick={()=>setShow(v=>!v)}>MIDDLE</button> {/* 在这里控制Bottom的挂载/卸载 */} {show && <Bottom />} </div> ); } function Bottom() { const state = useRef(0); useEffect(() => { console.log('Hey', state.current++); // 可选:组件卸载时的清理逻辑 return () => { console.log('Bottom卸载'); }; }, []); // 空依赖只在挂载时执行一次 return <div>BOTTOM</div>; }
改完之后点击按钮:
- show为true时,Bottom挂载,useRef初始化0,执行useEffect打印
Hey 0 - show为false时,Bottom卸载,useRef的引用被销毁
- 再点show变true,Bottom重新挂载,useRef重新初始化0,打印
Hey 0
额外提醒
- 组件的挂载/卸载看的是它有没有被父组件渲染到树里,和内部子元素显隐没关系。
- 你之前的useEffect没加依赖数组,每次组件渲染都会执行——show变化会让Middle重新渲染,Bottom也跟着重渲染,所以每次点按钮都会触发useEffect,这也是数值一直涨的原因之一。如果只需要挂载时执行一次,给useEffect加个空依赖数组
[]就行。
内容的提问来源于stack exchange,提问作者Unknown developer
相关产品推荐
相关产品推荐

