You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Redux注册功能报500错误与TypeError问题排查

注册功能报错问题

报错信息

  • 控制台抛出错误:RegisterPage.js:30 Uncaught (in promise) TypeError: Cannot read properties of undefined
  • 接口500响应内容:{"err": "E11000 duplicate key error collection: private.users index: email_1 dup key: { email: \"samarkan@mail.com\" }"}

场景:使用Redux实现用户注册、获取用户信息逻辑时,注册请求返回500状态码,附全部相关代码如下,求修复方案。

附原有代码

userReducer.js

import Axios from "axios";


export const register = ({name, fname, phone, email, password}) => async (dispatch)=> {
    dispatch({type: "USER_REGISTER_REQUEST", payload: {name, fname, phone, email}});
    try {
        const {data} = await Axios.post("api/users/register",{name, fname, phone, email})

        dispatch({type: "USER_REGISTER_SUCCESS", payload: data })
    } catch (error) {
        dispatch({type: "USER_REGISTER_FAIL", payload: error.response && error.response.data.message ? error.response.data.message : error.message})
    }
};

export const registerReducer = (state = {loading: true, userinfo: {}}, action) => {
  switch(action.type) {
    case "USER_REGISTER_REQUEST":
        return {loading: true}
        case "USER_REGISTER_SUCCESS":
            return {loading: false, userInfo: action.payload};
            case "USER_REGISTER_FAIL":
                return {loading: false, error: action.payload}
        default:
            return state;
  }
};

RegisterPage.js

import React from "react";
import {useDispatch, useSelector} from "react-redux";
import InputGroup from "./common/InputGroup";
import {useState, useEffect} from "react";
import {register} from "../reducers/userReducer";
import {useNavigate, useParams} from "react-router-dom";

const RegisterPage = () => {
    const dispatch = useDispatch();
   
   const [name, setName] = useState('');
   const [fname, setFname] = useState('');
   const [phone, setPhone] = useState('');
   const [email, setEmail] = useState('');
   const [password, setPassword] = useState('');
   const [confirm, setConfirm] = useState('');
   const navigate = useNavigate();
   const validateEmail = (email) => {
    return String(email)
      .toLowerCase()
      .match(
        /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
      );
  };


  const submitHandler = (e) => {
    if(password === confirm && validateEmail(email)) { 
     
        dispatch(register({name, fname, phone, email})).then((user)=> navigate({pathname: "/profile", search: `?id=${user._id}&name=${user.name}&fname=${user.fname}&email=${user.email}`}) );
    
      
   
    } else {
        window.alert("password and confirm password not the same try again or email not conform!")
    }
  }

  
  
    return (
        <div>
        <h2>Welcome to private <span>Let's be private</span></h2>
       
        <p id="icon" >Pr</p>
        <div className="form">
            <InputGroup value={name} type="text" label="name" name="name" onChange={(e)=> setName(e.target.value)}  />
            <InputGroup value={fname} type="text" label="fname" name="fname" onChange={(e)=> setFname(e.target.value)}/>
            <InputGroup value={phone} type="phone" label="phone" name="phone" onChange={(e)=> setPhone(e.target.value)}/>
            <InputGroup value={email} type="email" label="email" name="email" onChange={(e)=> setEmail(e.target.value)}/>
            <InputGroup value={password} type="password" label="password" name="password"  onChange={(e)=> setPassword(e.target.value)} />
            <InputGroup value={confirm} type="password" label="confirm password" name="confirm"  onChange={(e)=> setConfirm(e.target.value)} />
            <button onClick={(e)=>submitHandler()} >Register</button>
        </div>
        </div>
    )
};
export default RegisterPage;

store.js

import { createStore } from "redux";
import {compose, applyMiddleware} from "redux";
import {combineReducers} from "redux";

import thunk from "redux-thunk";
import {registerReducer} from "./reducers/userReducer";


const initialState = {user: {}};



const reducer = combineReducers({
    
user: registerReducer
    

});

const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore( reducer,  initialState, composeEnhancer(applyMiddleware(thunk)))
export default store;

问题原因&修复方法

一共6个问题,逐个修改即可:

  1. 接口漏传参数:register方法接收了password参数,但post请求体里没带password,后端拿不到密码字段会触发逻辑异常,请求时需要把password加到请求体里。
  2. 错误字段匹配错误:后端返回的错误信息存在err字段里,前端错误处理写的是取data.message,拿不到具体报错,需要把错误判断逻辑改成同时兼容data.err和data.message。
  3. thunk无返回值+未抛出错误:原有register thunk请求成功后没有return用户数据,请求失败也没有抛出reject,导致dispatch之后的.then拿到undefined,访问user._id直接触发类型错误。需要修改thunk逻辑:成功时return响应数据,失败时throw错误,避免无意义的.then执行。
  4. reducer字段拼写不一致:初始state写的是userinfo(i小写),成功分支写的是userInfo(I大写),字段不匹配会导致后续取状态拿不到值,需要统一字段名为userInfo。
  5. 跳转逻辑不合理:不要在dispatch的回调里拿数据跳转,用useSelector从redux store取userInfo和error状态,通过useEffect监听状态变化:注册成功拿到userInfo再跳转,出现错误直接弹提示。
  6. 500报错直接原因:MongoDB给email字段加了唯一索引,提交的邮箱samarkan@mail.com已经在库里存在了,因为后端没做重复邮箱的前置校验,直接抛了数据库层的重复键错误,可以在后端加一层重复邮箱判断,返回更友好的提示。

修复后核心代码参考

修复后的userReducer.js

import Axios from "axios";

export const register = ({name, fname, phone, email, password}) => async (dispatch)=> {
    dispatch({type: "USER_REGISTER_REQUEST", payload: {name, fname, phone, email}});
    try {
        // 补上password参数,接口路径前加/避免相对路径异常
        const {data} = await Axios.post("/api/users/register",{name, fname, phone, email, password})
        dispatch({type: "USER_REGISTER_SUCCESS", payload: data })
        // 成功时返回用户数据
        return data
    } catch (error) {
        // 兼容后端返回的err字段
        const errMsg = error.response?.data?.err || error.response?.data?.message || error.message
        dispatch({type: "USER_REGISTER_FAIL", payload: errMsg})
        // 失败时抛出错误,阻断后续.then执行
        return Promise.reject(errMsg)
    }
};

export const registerReducer = (state = {loading: false, userInfo: {}}, action) => {
  switch(action.type) {
    case "USER_REGISTER_REQUEST":
        return {loading: true, userInfo: {}}
    case "USER_REGISTER_SUCCESS":
        return {loading: false, userInfo: action.payload};
    case "USER_REGISTER_FAIL":
        return {loading: false, error: action.payload, userInfo: {}}
    default:
        return state;
  }
};

修复后的RegisterPage.js核心逻辑

// 从store取状态
const { loading, userInfo, error } = useSelector(state => state.user)
const navigate = useNavigate();

// 监听状态变化处理跳转和错误提示
useEffect(() => {
  if(userInfo?._id) {
    navigate(`/profile?id=${userInfo._id}&name=${userInfo.name}&fname=${userInfo.fname}&email=${userInfo.email}`)
  }
  if(error) {
    window.alert(error)
  }
}, [userInfo, error, navigate])

const submitHandler = (e) => {
  e.preventDefault()
  if(password !== confirm || !validateEmail(email)) {
    window.alert("两次输入密码不一致或邮箱格式错误!")
    return
  }
  // 提交时把password传进去,不需要在这里链式调用跳转
  dispatch(register({name, fname, phone, email, password}))
}

内容的提问来源于stack exchange,提问作者Borhen Kalboussi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 04:09:31