React Redux类组件TypeError:无法读取undefined的params属性解决方法
问题:TypeError: Cannot read properties of undefined (reading 'params')
尝试通过id获取用户详情数据时出现上述错误,相关代码如下:
app.js
import React, { Component } from 'react' import Jumbotron from './component/Jumbotron' import NavbarComponent from './component/NavbarComponent' import { BrowserRouter, Route, Routes, } from "react-router-dom"; import HomeContainer from './container/HomeContainer'; import CreateUserContainer from './container/CreateUserContainer'; import EditUserContainer from './container/EditUserContainer'; import DetailUserContainer from './container/DetailUserContainer'; export default class App extends Component { render() { return ( <div> <BrowserRouter> <NavbarComponent /> <Jumbotron /> <Routes> <Route path='/' element={<HomeContainer/>} exact /> <Route path='/create' element={<CreateUserContainer />} exact /> <Route path='/edit/:id' element={<EditUserContainer />} exact /> <Route path='/detail/:id' element={<DetailUserContainer />} exact /> </Routes> </BrowserRouter> </div> ) } }
container/DetailUserContainer.js
import React, { Component } from 'react' import { Container } from 'react-bootstrap' import { connect } from 'react-redux'; import { getDetailUser } from '../actions/UserAction'; class DetailUserContainer extends Component { componentDidMount(){ this.props.dispatch(getDetailUser(this.props.match.params.id)); } render() { return ( <div> <Container className='pt-3'> <div> <h1>Detail</h1> </div> </Container> </div> ) } } export default connect()(DetailUserContainer)
reducers/users.js
import { GET_LIST_USERS, GET_DETAIL_USERS } from "../actions/UserAction"; let initialState = { getUsers: false, errorGetUser: false, // DETAIL USER getDetailUsers: false, errorGetDetailUser: false } const users = (state = initialState, action) => { switch (action.type) { case GET_LIST_USERS: return { ...state, getUsers: action.payload.data, errorGetUser: action.payload.errorMessage }; case GET_DETAIL_USERS: return { ...state, getDetailUsers: action.payload.data, errorGetDetailUser: action.payload.errorMessage }; default: return state; } } export default users;
actions/UserAction.js
import React from 'react' import axios from 'axios' export const GET_LIST_USERS = "GET_LIST_USERS" export const GET_DETAIL_USERS = "GET_DETAIL_USERS" // GET USER export const getListUser= () => { return (dispatch) => { axios.get('https://my-json-server.typicode.com/baihaqiyazid/database/users') .then(function (response) { dispatch({ type: GET_LIST_USERS, payload: { data: response.data, errorMessage: false } }) }) .catch(function (error) { dispatch({ type: GET_LIST_USERS, payload: { data: false, errorMessage: error.message } }) }) } } // DETAIL USER export const getDetailUser= (id) => { return (dispatch) => { axios.get('https://my-json-server.typicode.com/baihaqiyazid/database/users/' + id) .then(function (response) { dispatch({ type: GET_DETAIL_USERS, payload: { data: response.data, errorMessage: false } }) }) .catch(function (error) { dispatch({ type: GET_DETAIL_USERS, payload: { data: false, errorMessage: error.message } }) }) } }
reducers/index.js
import { combineReducers } from 'redux' import users from './users' export default combineReducers({ users })
解决方案
错误原因
你使用的是React Router v6,该版本中Route组件的element属性接收的组件不再自动注入match、location、history等路由相关props,因此this.props.match为undefined,读取params时触发报错。
方案1:封装自定义withRouter高阶组件(适配类组件)
React Router v6移除了官方withRouter,可自行封装一个将路由props注入类组件:
- 创建
withRouter.js文件:
import { useParams, useLocation, useNavigate } from 'react-router-dom'; const withRouter = (Component) => { const Wrapper = (props) => { const params = useParams(); const location = useLocation(); const navigate = useNavigate(); return <Component {...props} match={{ params }} location={location} navigate={navigate} />; }; return Wrapper; }; export default withRouter;
- 修改
DetailUserContainer.js,用withRouter包裹连接Redux后的组件:
import React, { Component } from 'react' import { Container } from 'react-bootstrap' import { connect } from 'react-redux'; import { getDetailUser } from '../actions/UserAction'; import withRouter from '../withRouter'; // 引入自定义withRouter class DetailUserContainer extends Component { componentDidMount(){ this.props.dispatch(getDetailUser(this.props.match.params.id)); } render() { return ( <div> <Container className='pt-3'> <div> <h1>Detail</h1> </div> </Container> </div> ) } } // 先connect再用withRouter包裹 export default withRouter(connect()(DetailUserContainer));
方案2:改为函数组件,使用useParams钩子(推荐)
React Router v6更推荐函数组件配合路由钩子,写法更简洁:
修改DetailUserContainer.js为函数组件:
import React, { useEffect } from 'react' import { Container } from 'react-bootstrap' import { connect } from 'react-redux'; import { getDetailUser } from '../actions/UserAction'; import { useParams } from 'react-router-dom'; const DetailUserContainer = (props) => { const { id } = useParams(); // 获取路由参数id useEffect(() => { props.dispatch(getDetailUser(id)); }, [id, props.dispatch]) return ( <div> <Container className='pt-3'> <div> <h1>Detail</h1> </div> </Container> </div> ) } export default connect()(DetailUserContainer)
两种方案均可解决问题,方案2更符合React Router v6的设计趋势,建议优先采用。
内容的提问来源于stack exchange,提问作者leo
相关产品推荐
相关产品推荐

