useSelector重渲染后无法获取Redux最新state值返回null如何解决?
问题描述
我正在开发用户页面,通过Redux的useSelector hook从状态中获取用户信息,代码如下:
import React, { useState, useEffect } from "react"; import profileImage from "../../resources/images/defaultProfile.png"; import { useSelector, useDispatch } from "react-redux"; import { userActions } from "../../state/actions"; const User = () => { const [isEditable, setIsEditable] = useState(false); const onChange = (e) => { setUser({ ...user, [e.target.name]: [e.target.value], }); }; const onSubmit = () => { if (!isEditable) { setIsEditable(!isEditable); } else { //TODO update user data here } }; const [user, setUser] = useState(useSelector((state) => state.user.user)); return ( user !== null && ( <div className="container-fluid"> <div className="row"> <div className="col-md-9 border border-primary">user preference</div> <div className="col-md-3 border border-primary"> <div className="row-cols-md-3 text-center"> <img src={profileImage} alt="Profile image" style={{ width: "60%", height: "70%" }} className="border rounded-circle" /> <form onSubmit={onSubmit} style={{ width: "100%" }}> <div className="mb-3"> <label htmlFor="userName" className="form-label"> User Name </label> <input type="text" name="name" id="userName" value={user.name} onChange={onChange} disabled={!isEditable ? "disabled" : ""} className="form-control" /> </div> <div className="mb-3"> <label htmlFor="userEmail" className="form-label"> User Email </label> <input type="email" name="email" disabled={!isEditable ? "disabled" : ""} id="userEmail" value={user.email} onChange={onChange} className="form-control" /> </div> <div className="mb-3"> <label htmlFor="userPhone" className="form-label"> User Phone Number </label> <input type="text" disabled={!isEditable ? "disabled" : ""} name="phoneno" id="userPhone" value={user.phoneno} onChange={onChange} className="form-control" /> </div> <button type="submit">{isEditable ? "Update" : "Edit"}</button> </form> </div> </div> </div> </div> ) ); }; export default User;
页面首次加载时数据正常,初始所有输入框均为禁用状态,点击编辑按钮后会修改isEditable状态启用所有输入框,提交按钮切换为「更新」,此时isEditable状态变更会触发页面重新渲染。我所有页面默认加载导航栏,导航栏中包含页面刷新时重新加载用户状态的逻辑,页面重新渲染时会重载全局用户状态,但不知为何useSelector无法获取最新值,仅返回null。
观察结论:尽管Redux中已经存在最新的状态值,完整重渲染完成后useSelector仍然无法拉取到最新的用户数据。请问应该采用什么方案解决该问题?
解决方案
根因说明
核心问题是将useSelector的返回值直接作为useState的初始值,useState的初始值仅在组件首次挂载时计算一次,后续Redux store中的user状态更新时,组件本地的user状态不会自动同步,就会出现Redux已经有最新值,但本地读取的还是旧值/初始null的情况。
修复代码
不需要把Redux的user存到本地冗余状态,直接读取useSelector的结果即可,编辑状态下的临时修改单独存本地状态,提交时再同步到Redux:
import React, { useState, useEffect } from "react"; import profileImage from "../../resources/images/defaultProfile.png"; import { useSelector, useDispatch } from "react-redux"; import { userActions } from "../../state/actions"; const User = () => { const [isEditable, setIsEditable] = useState(false); // 直接从Redux取最新用户数据,不要作为本地state的初始值 const reduxUser = useSelector((state) => state.user.user); const dispatch = useDispatch(); // 单独存储编辑状态下的临时用户数据 const [editUser, setEditUser] = useState(null); // 进入编辑模式时同步最新的Redux用户数据到本地编辑状态 useEffect(() => { if (isEditable && reduxUser) { setEditUser({...reduxUser}); } }, [isEditable, reduxUser]); const onChange = (e) => { setEditUser({ ...editUser, // 原代码多套了一层数组,已移除,避免值变成数组格式 [e.target.name]: e.target.value, }); }; const onSubmit = (e) => { e.preventDefault(); if (!isEditable) { setIsEditable(true); } else { // 提交时把编辑好的用户数据更新到Redux dispatch(userActions.updateUser(editUser)); setIsEditable(false); } }; // 渲染用的用户数据:编辑模式用本地临时数据,非编辑模式用Redux源数据 const showUser = isEditable ? editUser : reduxUser; return ( showUser !== null && ( <div className="container-fluid"> <div className="row"> <div className="col-md-9 border border-primary">user preference</div> <div className="col-md-3 border border-primary"> <div className="row-cols-md-3 text-center"> <img src={profileImage} alt="Profile image" style={{ width: "60%", height: "70%" }} className="border rounded-circle" /> <form onSubmit={onSubmit} style={{ width: "100%" }}> <div className="mb-3"> <label htmlFor="userName" className="form-label"> User Name </label> <input type="text" name="name" id="userName" value={showUser.name} onChange={onChange} // disabled直接传布尔值即可,无需字符串 disabled={!isEditable} className="form-control" /> </div> <div className="mb-3"> <label htmlFor="userEmail" className="form-label"> User Email </label> <input type="email" name="email" disabled={!isEditable} id="userEmail" value={showUser.email} onChange={onChange} className="form-control" /> </div> <div className="mb-3"> <label htmlFor="userPhone" className="form-label"> User Phone Number </label> <input type="text" disabled={!isEditable} name="phoneno" id="userPhone" value={showUser.phoneno} onChange={onChange} className="form-control" /> </div> <button type="submit">{isEditable ? "Update" : "Edit"}</button> </form> </div> </div> </div> </div> ) ); }; export default User;
额外修复的问题点
- 原
onChange方法中给字段赋值多套了一层数组,会导致输入的字符串变成数组格式,提交时出错,已移除冗余的数组包裹 disabled属性简化为直接传布尔值,无需额外拼接字符串- 拆分了Redux源数据和编辑临时数据,避免编辑过程中未提交的修改直接污染全局Redux状态
内容的提问来源于stack exchange,提问作者Sandeep Bisht
相关产品推荐
相关产品推荐

