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

React Redux登录功能异常:无法跳转Profile页及显示错误

React Redux登录功能异常排查求助

我用React Redux实现了注册和登录功能,期望登录成功后跳转至Profile页面,出错时显示错误信息。注册页功能正常,但登录页既不跳转也不显示错误提示。两者代码几乎一致,作为新手不知道怎么调试。控制台显示useSelector获取state.auth.user为空,但Profile页的相同useSelector代码却能正常工作。

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import "./index.css"
import axios from 'axios'
import { store } from './store/store.jsx'
import { Provider } from 'react-redux'
import { createBrowserRouter, createRoutesFromElements, Route, RouterProvider } from 'react-router-dom'
import SignupForm from './Components/signup'
import SigninForm from './Components/signin'
import RootLayout from './Components/root-layout'
import ErrorPage from './Components/error-page'
import ProfilePage from './Components/profile'

const router = createBrowserRouter(
  createRoutesFromElements((
    <Route path='/' element={<RootLayout />}>
      <Route path='/' element={<App />}></Route>
      <Route path='/signup' element={<SignupForm />} ></Route>
      <Route path='/signin' element={<SigninForm />} ></Route>
      <Route path='*' element={<ErrorPage />}></Route>
      <Route path='/profile' element={<ProfilePage />}></Route>
    </Route>
  )
))

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
  <Provider store={store}>
    <RouterProvider router={router} />
  </Provider>
</React.StrictMode>,
)

store.jsx

import { configureStore } from '@reduxjs/toolkit'
import authSlice from './authSlice'

export const store = configureStore({
  reducer: {
    auth: authSlice
  }
})

authSlice.jsx

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";

export const signup = createAsyncThunk('auth/signup', async({username, password}, thunkAPI) => {
  try {
    const res = await axios.post('http://localhost:8080/signup', {username, password})
    return res.data
  } catch (err) {
    console.log(err)
    return thunkAPI.rejectWithValue(err.message)
  }
})

export const signin = createAsyncThunk('auth/signin', async({username, password}) => {
  try {
    const res = await axios.post('http://localhost:8080/signin', {username, password})
    return res.data
  } catch (err) {
    return thunkAPI.rejectWithValue(err.response.data)
  }
})

const initialState = {
  user: '',
  isLoggedIn: false,
  loading: false,
  error:null
}

export const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    logout: (state, action) => {
      state.user = ''
      state.isLoggedIn = false
      state.loading = false
      state.error = null
    }
  },
  extraReducers: (builder) => {
    builder
      .addCase(signup.fulfilled, (state, action) => {
        state.user = action.payload.username
        state.isLoggedIn = true
        state.loading = false
        state.error = null
      })
      .addCase(signup.pending, (state, action) => {
        state.loading = true
      })
      .addCase(signup.rejected, (state, action) => {
        state.loading = false
        state.isLoggedIn = false
        state.error = action.payload
      })
      .addCase(signin.fulfilled, (state, action) => {
        state.user = action.payload.username
        state.isLoggedIn = true
        state.loading = false
        state.error = null
      })
      .addCase(signin.pending, (state, action) => {
        state.loading = true
      })
      .addCase(signin.rejected, (state, action) => {
        state.loading = false
        state.isLoggedIn = false
        state.error = action.payload
      })
  }
})

export const { logout } = authSlice.actions

export default authSlice.reducer

profile.jsx

import { useSelector } from "react-redux"

export default function ProfilePage () {
  const user = useSelector((state) => state.auth.user) //这个useSelector能正常工作
  return (
    <div>
      <h3>Profile</h3>
      {user ? <div>Hi, {user}!</div> : null}
    </div>
  )
}

signup.jsx

import { useDispatch, useSelector } from "react-redux";
import { Navigate } from "react-router-dom";
import { signup } from "../store/authSlice";

export default function SignupForm () {
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const user = useSelector((state) => state.auth.user)
  const error = useSelector((state) => state.auth.error)
//这两个useSelector在console.log时看似不工作,但功能正常
  const dispatch = useDispatch()

  const submitHandler = e => {
    e.preventDefault()
    dispatch(signup({username, password}))
    .then((res) => {
      setPassword('')
      setUsername('')
    })
    console.log(user)
    console.log(error)
  }

  return (
    <div>
      <form onSubmit={submitHandler}>
        <div>Sign up</div>
        <div>
          <label htmlFor="username">Username:</label>
          <input id="username" type="text" value={username} onChange={(e) => setUsername(e.target.value)} minLength={1} maxLength={60}/>
        </div>
        <div>
          <label htmlFor="password">Password:</label>
          <input id="password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} minLength={1}/>
        </div>
        <div>
          <button>Cancel</button>
          <button type="submit">Submit</button>
        </div>
        {error ? <p>{error}</p> : null}
        {user ? <Navigate to='/profile' replace={true} /> : null}
      </form>
    </div>
  )
}

signin.jsx

import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Navigate } from "react-router-dom";
import { signin } from "../store/authSlice";

export default function SigninForm () {
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const user = useSelector((state) => state.auth.user)
  const error = useSelector((state) => state.auth.error)
//这两个useSelector不工作 ^
  const dispatch = useDispatch()

  const submitHandler = e => {
    e.preventDefault()
    dispatch(signin({username, password}))
    .then((res) => {
      setPassword('')
      setUsername('')
    })
    console.log(user)
    console.log(error)
  }

  return (
    <div>
      <form onSubmit={submitHandler}>
        <div>Sign in</div>
        <div>
          <label htmlFor="username">Username:</label>
          <input id="username" type="text" value={username} onChange={(e) => setUsername(e.target.value)} minLength={1} maxLength={60}/>
        </div>
        <div>
          <label htmlFor="password">Password:</label>
          <input id="password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} minLength={1}/>
        </div>
        <div>
          <button>Cancel</button>
          <button type="submit">Submit</button>
        </div>
        {error ? <div>{error}</div> : null}
        {user ? <Navigate to='/profile' replace={true} /> : null}
//这两个部分在注册页正常,登录页失效 ^
      </form>
    </div>
  )
}

控制台输出

登录页console.log显示user为空字符串,error为null。


问题修复方案

1. 修复signin异步Thunk参数缺失

authSlice.jsx中signin异步Thunk未接收thunkAPI参数,导致错误处理时无法调用thunkAPI.rejectWithValue,修正如下:

export const signin = createAsyncThunk('auth/signin', async({username, password}, thunkAPI) => {
  try {
    const res = await axios.post('http://localhost:8080/signin', {username, password})
    return res.data
  } catch (err) {
    // 兼容接口返回异常的情况
    return thunkAPI.rejectWithValue(err.response?.data || err.message)
  }
})

2. 校验登录接口返回格式

确保后端/signin接口返回的数据结构包含username字段,与注册接口一致。若返回格式不同(比如{user: {username: 'xxx'}}),需同步修改signin.fulfilled逻辑:

.addCase(signin.fulfilled, (state, action) => {
  // 根据实际返回结构调整取值路径
  state.user = action.payload.username || action.payload.user?.username
  state.isLoggedIn = true
  state.loading = false
  state.error = null
})

3. 修正组件状态监听逻辑

组件内console.log在dispatch执行后立即打印,此时Redux状态尚未更新,属于正常现象。可改用useEffect监听状态变化:

// 在signin.jsx中添加
import { useEffect } from 'react';

useEffect(() => {
  console.log('当前user:', user)
  console.log('当前error:', error)
}, [user, error])

4. 补充signup.jsx缺失的导入

signup.jsx未导入useState,会导致运行错误,需补充:

import React, { useState } from "react";

内容的提问来源于stack exchange,提问作者Cian Neiland

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:47:03