React按下Enter触发函数时获取过时状态的问题
React + MobX + MaterialUI 状态变量异常问题
- 触发场景差异:点击按钮调用
handleSendPing时,selectedUser等状态变量能正确转换为NewPingDto对象;按Enter键触发该函数时,对象的recipient、locationName、pingMessage字段为空,但控制台单独打印的状态值是正确的。 - 排除情况:页面无表单元素,所有按钮均设置
type="button",可排除意外提交导致的状态重置。
相关代码
useEffect(() => { window.addEventListener("keydown", handleKeyPress); return () => { window.removeEventListener("keydown", handleKeyPress); }; }, []); const handleKeyPress = (e: any) => { if (e.key === "Enter") { e.preventDefault(); handleSendPing(); } }; const handleSendPing = () => { if (userStore.user) { var ping: NewPingDto = { recipient: selectedUser, locationName: selectedLocation, pingMessage: selectedPingMessage, senderId: userStore.user.id, companyId: userStore.user.companyId, dateCreated: new Date(), dateLastModified: new Date(), isExpired: false, isUrgent: urgentPing, pingResponse: "", isRead: false, }; console.log("foo", ping); pingStore.addPing(ping); } }; useEffect(() => { console.log("new selectedUser", selectedUser); }, [selectedUser]);
Enter触发时的输出结果
{ "recipient": "", "locationName": "", "pingMessage": "", "senderId": "c0a0d5a0-ffaf-4b7e-8f4a-6b6b8b7b8b71", "companyId": 1, "dateCreated": "2023-08-21T17:39:25.014Z", "dateLastModified": "2023-08-21T17:39:25.014Z", "isExpired": false, "isUrgent": false, "pingResponse": "", "isRead": false }
问题原因
绑定window键盘事件的useEffect依赖数组为空,组件挂载后仅执行一次事件绑定。此时handleKeyPress函数内部引用的selectedUser等状态变量是组件初始渲染时的值(空字符串),后续状态更新时不会重新绑定事件,导致按Enter触发时仍使用旧的状态值。
解决方法
方案1:更新事件绑定的依赖,确保回调引用最新状态
将handleKeyPress定义在useEffect内部,并把handleSendPing加入依赖数组,每次handleSendPing更新时重新绑定事件:
useEffect(() => { const handleKeyPress = (e: any) => { if (e.key === "Enter") { e.preventDefault(); handleSendPing(); } }; window.addEventListener("keydown", handleKeyPress); return () => { window.removeEventListener("keydown", handleKeyPress); }; }, [handleSendPing]);
方案2:用useCallback稳定handleSendPing的引用
给handleSendPing加上useCallback包裹,明确它的依赖项,避免不必要的重新创建,同时确保事件回调能获取最新的状态值:
const handleSendPing = useCallback(() => { if (userStore.user) { var ping: NewPingDto = { recipient: selectedUser, locationName: selectedLocation, pingMessage: selectedPingMessage, senderId: userStore.user.id, companyId: userStore.user.companyId, dateCreated: new Date(), dateLastModified: new Date(), isExpired: false, isUrgent: urgentPing, pingResponse: "", isRead: false, }; console.log("foo", ping); pingStore.addPing(ping); } }, [userStore.user, selectedUser, selectedLocation, selectedPingMessage, urgentPing, pingStore]);
两种方案结合使用,即可解决按Enter触发时状态变量为空的问题。
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

