You need to enable JavaScript to run this app. React Redux activePage切换报'type' of undefined错误
优惠活动
大模型
产品
解决方案
定价
更多

React Redux activePage切换报'type' of undefined错误

核心报错根因

你遇到的TypeError: Cannot read property 'type' of undefined,触发逻辑是:reducer执行时拿到的action参数值为undefined,读取action.type时自然触发属性访问异常。这个问题不是单个点的bug,是你使用React-Redux的connect、reducer编写规则时把几个核心API的用法搞反了,叠加缺失reducer的兜底逻辑共同导致的。

逐文件定位的问题与修正

1. loginButton.js 核心API用反

原代码的问题:

  • 你把mapDispatchToProps的入参写成了state,实际上mapDispatchToProps的入参是dispatch方法,返回值里的方法要用来触发状态更新;现在的写法返回的changePage直接取了state.activePage,本质是个字符串值,不是action创建函数。
  • 按钮点击时调用changePage('loginPage'),本质是对字符串做函数调用,执行后返回undefined,这个undefined会被当成action传给dispatch,最后流入reducer时直接触发访问action.type的报错。
    修正方式:
    直接用React-Redux提供的简写connect写法,把action创建函数作为connect的第二个参数传入,框架会自动帮你把每个action绑定dispatch方法,不需要自己手动写mapDispatch逻辑。

2. reducer/index.js 缺失强制兜底逻辑

原代码的问题:

  • switch语句只匹配了CHANGE_PAGE这一个case,没有设置default分支返回原state。Redux在store初始化时会自动dispatch一个带初始化type的内置action,所有不匹配当前case的action(包括初始化时的内置action、前面传进来的undefined非法action)都会走到函数结尾,返回undefined,直接把整个store的初始state覆盖为空值,从根源上触发后续的属性访问报错。
  • 文件里冗余导入了changePage action函数,reducer本身不需要调用action创建函数,只需要根据传入的action对象的type、payload更新state即可,这个导入完全多余。
    修正方式:
  • 在switch的最后加default分支,把当前的state原封不动返回,这是Redux对reducer的强制要求。
  • 删掉冗余的action导入。

3. App.js 状态取值逻辑错误

原代码的问题:

  • 页面渲染判断时读取的是this.props.changePage,但changePage是用来触发状态更新的action函数,根本不是页面状态值;哪怕你把connect的逻辑接对了,用函数和页面名字符串做相等判断永远不会命中,自然不会正确切换页面。
    修正方式:
  • 通过connect把store里存储的activePage状态值映射到App组件的props上,用这个值判断要渲染的页面。

4. index.js 组件嵌套结构混乱

原代码的问题:

  • App组件被放在BrowserRouter的路由出口外面,嵌套层级不对,导致组件拿不到路由上下文;整体Provider、Router、组件的嵌套顺序有问题。
    修正方式:
  • 调整嵌套顺序:最外层是Provider(注入store),里面包BrowserRouter(注入路由能力),最内层放App组件,保证所有子组件能拿到对应的context。

修正后的可运行代码

1. constants/action-types.js(原代码逻辑正确,无需修改)

export const CHANGE_PAGE = "CHANGE_PAGE";

2. actions/actions.js(原代码逻辑正确,无需修改)

import { CHANGE_PAGE } from "../constants/action-types";
export const changePage = (activePage) => ({
  type: CHANGE_PAGE,
  payload: {
    activePage,
  },
});

3. redux/reducer/index.js(修正后)

import { CHANGE_PAGE } from "../constants/action-types";
const initState = {
  activePage: 'HomePage',
}
function allReducers(state = initState, action){
  switch(action.type){
    case CHANGE_PAGE:
      return {
        ...state,
        activePage: action.payload.activePage,
      };
    // 增加兜底返回逻辑
    default:
      return state;
  }
}
export default allReducers;

4. components/LoginButton.js(修正后)

import { connect } from 'react-redux';
import { changePage } from '../redux/actions/actions';

function LoginButtonThing (props) {
  return(
    <div>
      <button onClick={() => props.changePage('LoginPage')}>Login Page</button>
    </div>
  )
}
// 第一个参数传null表示不需要读取store状态,第二个参数直接传入action对象
// react-redux会自动把action函数绑定到dispatch上
export default connect(null, { changePage })(LoginButtonThing);

5. App.js(修正后)

import { connect } from 'react-redux';
import LoginPage from './loginPage';
import HomePage from './homePage';
import LoginButton from './components/LoginButton';

class App extends React.Component {
  render() {
    let renderedPage;
    // 从props读取映射过来的activePage状态值做判断
    if (this.props.activePage === "LoginPage") {
      renderedPage = <LoginPage/>;
    } else if (this.props.activePage === "HomePage") {
      renderedPage = <HomePage/>;
    } else {
      renderedPage = <HomePage/>;
    }
    return (
      <div id="App">
        <LoginButton />
        {renderedPage}
      </div>
    );
  }
}
// 把store里的activePage状态映射到组件props
const mapStateToProps = (state) => {
  return {
    activePage: state.activePage,
  }
}
export default connect(mapStateToProps)(App);

6. index.js(修正后)

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import allReducers from "./redux/reducer";
import App from './App';

const store = createStore(
  allReducers,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

ReactDOM.render(
  <Provider store={store}>
    <React.StrictMode>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </React.StrictMode>
  </Provider>,
  document.getElementById('root')
);

核心使用规则提醒

  • 记牢connect两个参数的边界:第一个参数是mapStateToProps,负责把store里的状态映射到组件props,入参是state;第二个参数是mapDispatchToProps,负责把dispatch方法映射到组件props,入参是dispatch,两个的入参、返回值完全不能搞混。
  • 写reducer必须遵守纯函数规则:不能修改原state,不匹配action的时候必须返回原state,绝对不能返回undefined,否则整个store会变成空值。
  • 只要用connect把action对象传进去,react-redux会自动生成包装好的、绑定了dispatch的action触发函数,不需要手动写dispatch调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.25 09:54:04