React Hook setState搭配react-hook-popup使用时状态无法更新问题
问题原因
react-hook-popup的usePopup Hook在组件首次渲染时就会注册弹窗的渲染函数,该函数形成的闭包只会捕获首次渲染时的状态变量值,后续组件状态更新时,弹窗内部的渲染函数不会同步更新,所以你看到的永远是初始的newChore和newChoreIndex值,只有重新调用showPopup时才会传入最新的参数。
解决方案
- 方案1:将弹窗需要用到的状态通过
showPopup参数传入,把弹窗变成无状态组件
所有弹窗内需要的状态都在调用showPopup的时候作为参数传递,修改弹窗渲染函数只接收参数不直接引用组件的状态:
// 弹窗定义 const [showPopup, hidePopup] = usePopup('popup', ({ message, newChoreIndex, newChore, handleClose, onChoreChange, onUpdate }) => ( <div className="modal"> <div className="modalInner"> <button className="modalClose" onClick={handleClose}> X </button> <div className="modalInner2"> <p className="modalHeading">Previous Chore(s)</p> <p className="modalChore">{message}</p> <p className="modalHeading">New Chore {newChoreIndex}</p> <input className="newChoreInput" placeholder="New Chore" value={newChore} onChange={(e) => onChoreChange(e.target.value)} /> <button className="modalSubmit" onClick={() => { onUpdate() hidePopup() }}> Update Chore </button> </div> </div> </div> )); // 调用showPopup的时候传入所有需要的参数 onClick={() => { setNewChoreIndex(index) setNewChore('') // 打开编辑前重置输入 showPopup(item, newChoreIndex, newChore, (val)=>setNewChore(val), updateChore) }}
- 方案2:放弃使用react-hook-popup,改用受控组件实现弹窗
直接在组件内用状态控制弹窗显示隐藏,这样弹窗内部可以直接访问最新的组件状态,不需要额外处理闭包问题:
// 新增弹窗显示状态 const [popupVisible, setPopupVisible] = useState(false) const [currentChore, setCurrentChore] = useState('') // 渲染弹窗部分直接写在返回的JSX里,根据popupVisible控制显示 {popupVisible && <div className="modal"> {/* 弹窗内容和之前一样,直接引用newChore、newChoreIndex即可 */} </div>} // 打开弹窗的逻辑 onClick={() => { setNewChoreIndex(index) setNewChore('') setCurrentChore(item) setPopupVisible(true) }}
该方案逻辑更简单,也不会有闭包陷阱,适合对React Hooks不熟悉的场景。
- 方案3:使用useRef存储状态变量
如果一定要用react-hook-popup,可以把需要在弹窗内访问的状态存到ref里,ref的current属性是可变的,不会受闭包影响:
const [newChore, _setNewChore] = useState("") const newChoreRef = useRef(newChore) const setNewChore = (val) => { _setNewChore(val) newChoreRef.current = val } const [newChoreIndex, _setNewChoreIndex] = useState(-1) const newChoreIndexRef = useRef(newChoreIndex) const setNewChoreIndex = (val) => { _setNewChoreIndex(val) newChoreIndexRef.current = val } // 弹窗内访问ref的current值 <p className="modalHeading">New Chore {newChoreIndexRef.current}</p> <input className="newChoreInput" placeholder="New Chore" onChange={(e) => {setNewChore(e.target.value)}} />
内容的提问来源于stack exchange,提问作者Hossam Alsheikh
相关产品推荐
相关产品推荐

