React+Redux中判断空对象的条件语句未执行问题排查
问题根因
你遇到的判断不生效问题来自两个核心错误:
- 对象比较逻辑错误:JavaScript里的对象是引用类型,
===对比对象时校验的是两个变量是否指向同一块内存地址,不会校验对象内容。你写的props.errors === {}中,右侧的{}是执行到这行时临时创建的全新空对象,和Redux存储的errors对象内存地址完全不同,所以这个条件永远不会成立。 - 异步状态时序错误:
dispatch(register(newUser))触发的注册接口请求是异步操作,dispatch调用后不会等待接口返回、Redux状态更新完成,会立刻执行后面的判断代码。此时拿到的props.errors是组件上一次渲染绑定的旧状态,根本不是本次注册请求返回的最新结果,同步判断自然拿不到正确值。
另外你控制台打印出errors : [object Object]是因为用+拼接字符串和对象时,对象会自动调用toString()方法返回固定格式,不是errors值异常,要查看对象具体内容直接传参给console即可:console.log('errors :', props.errors)。
你当前的errorReducer逻辑没有问题,不需要修改。
修复方法
第一步:修正空对象判断逻辑
判断空对象不要用直接相等比较,用Object.keys(目标对象).length === 0判断对象是否没有自身可枚举属性即可。
第二步:把状态判断逻辑移到正确的执行时机
不要在dispatch后同步判断Redux状态,推荐两种成熟实现:
方式1:useEffect监听errors状态变化
Redux状态更新后会触发组件重渲染,最新的errors会通过props传入组件,在useEffect里判断就能拿到最新值,注意加提交标记避免组件初始化时误触发逻辑:
import { useRef, useEffect } from 'react'; // 组件内定义提交标记,存到ref里不会触发重渲染 const isSubmittingRef = useRef(false); // 监听errors变化 useEffect(() => { // 只有处于提交状态时才处理结果 if (!isSubmittingRef.current) return; if (Object.keys(props.errors).length === 0) { // 无错误,执行成功逻辑 setSnackOpen(true); setFname(''); setLname(''); setEmail(''); setPhone(''); setPassword(''); } else { // 有错误时可以在这里加错误提示逻辑,比如错误弹窗 } // 重置提交标记 isSubmittingRef.current = false; }, [props.errors]); function onSubmit(e) { e.preventDefault(); // 标记开始提交 isSubmittingRef.current = true; const newUser = { fname: fname, lname: lname, phone: phone, email: email, password: password, }; dispatch(register(newUser)); }
方式2:在thunk中返回Promise,直接在提交逻辑中处理结果
如果你的register是基于redux-thunk编写的异步action,可以在thunk里返回Promise,提交时直接用then/catch处理结果,不需要依赖props更新:
首先修改register thunk(示例):
export const register = (newUser) => (dispatch) => { return axios.post('/api/register', newUser) .then(res => { // 注册成功逻辑,比如存用户信息 return res.data; }) .catch(err => { dispatch({ type: GET_ERRORS, payload: err.response.data }); return Promise.reject(err); }) }
然后组件里的onSubmit可以直接写:
async function onSubmit(e) { e.preventDefault(); const newUser = { fname: fname, lname: lname, phone: phone, email: email, password: password, }; try { await dispatch(register(newUser)); // 走到这里说明请求成功无错误 setSnackOpen(true); setFname(''); setLname(''); setEmail(''); setPhone(''); setPassword(''); } catch (err) { // 请求失败,错误已经被dispatch到errors reducer,这里不需要额外处理状态 } }
内容的提问来源于stack exchange,提问作者SaLii
相关产品推荐
相关产品推荐

