React-Redux搜索栏报错:TypeError: Cannot read property 'map' of undefined
解决React + Redux搜索栏的
map未定义错误及异步状态管理问题 嘿,刚看完你的代码,咱们一步步把这个TypeError: Cannot read property 'map' of undefined错误解决掉,顺便把Redux异步状态管理的逻辑理顺~
问题根源拆解
你遇到的核心问题有几个,咱们逐个击破:
- Redux初始状态缺失
locations:你的clientReducer初始状态里只有client对象,没有locations数组,导致组件一开始拿不到这个值 - 拼写错误导致props映射失败:
clientDevicelocate.js的mapStateToProps里写了loactions(少了字母c),组件里还错误地用了this.props.location(单数),双重错误直接导致locations是undefined - Reducer未处理异步返回的状态:你在action里拿到了后端返回的locations,但reducer里没有把它存到state里
- SearchBar组件的dispatch绑定错误:你绑定了
setClient,但实际需要调用的是异步actionfetchClient
逐个文件修改方案
1. 修改clientReducer.js:初始化状态 + 处理action
import * as actionTypes from '../actions/actionTypes'; // 初始化locations为空数组,避免map时出现undefined const initialState ={ client: { numClientID:'', txtName:'' }, locations: [] // 新增:初始化空数组 }; const clientReducer = (state = initialState, action) => { switch(action.type){ case actionTypes.SEARCH_CLIENT: return{ ...state, locations: action.locations // 把action传递的locations存入state }; case actionTypes.FETCH_CLIENT_FAILED: return{ ...state, locations: [] // 请求失败时清空数组,避免后续错误 }; default: return state; } } export default clientReducer;
2. 修改clientDevicelocate.js:修正props映射 + 变量名
import React, { Component } from 'react'; import { connect } from 'react-redux'; import './Clientdevicelocate.css'; import {Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn,} from 'material-ui/Table'; class clientDevicelocate extends Component { render () { // 修正:取this.props.locations(复数,和mapStateToProps的key对应) let locations = this.props.locations; console.log('===================================='); console.log('Locations', locations); console.log('===================================='); return( <div> <Table> <TableHeader> <TableRow> <TableHeaderColumn>Device Alias</TableHeaderColumn> <TableHeaderColumn>Device Serial Number </TableHeaderColumn> <TableHeaderColumn>Device IMEI</TableHeaderColumn> <TableHeaderColumn>Locate Address</TableHeaderColumn> <TableHeaderColumn>Locate Date</TableHeaderColumn> <TableHeaderColumn>Locate Time</TableHeaderColumn> </TableRow> </TableHeader> <TableBody> {/* 因为初始是数组,即使请求未返回,map也不会报错 */} {locations.map(function(location, i){ return <TableRow key={location.numVehicleDeviceID}> <TableRowColumn>{location.txtAlias}</TableRowColumn> <TableRowColumn>{location.txtSMSDeviceSN}</TableRowColumn> <TableRowColumn>{location.IMEI}</TableRowColumn> <TableRowColumn>{location.fullAddress}</TableRowColumn> <TableRowColumn>{location.date}</TableRowColumn> <TableRowColumn>{location.time}</TableRowColumn> </TableRow> })} </TableBody> </Table> </div> ); } } const mapStateToProps = state => { return{ locations: state.locations // 修正拼写错误:loactions → locations }; } export default connect(mapStateToProps)(clientDevicelocate);
3. 修改searchClientAction.js:优化action参数命名(可选但更清晰)
import * as actionTypes from './actionTypes'; import axios from 'axios'; // 把参数名从value改成locations,语义更清晰 export const setClient = (locations) => { return{ type: actionTypes.SEARCH_CLIENT, locations: locations, // 和reducer里的key对应 }; }; export const fetchClientFailed = () => { return { type: actionTypes.FETCH_CLIENT_FAILED } } export const fetchClient = (value) => { return dispatch => { axios({ method: 'post', url: 'http://.../.../getDeviceLocation', data: { "numClientID" : value, "numContactID" : null, "ynActive" : true } }) .then((response) => { console.log(response) let locations = response.data; for(let i=0; i<locations.length; i++){ let location = locations[i]; locations[i].fullAddress = location.txtAddress + ', '+ location.txtCity + ', ' + location.txtState + ' ' +location.txtZip; locations[i].date = location.dtDate.substring(0,10); locations[i].time = location.dtDate.substring(11, 19); } dispatch(setClient(locations)); }) .catch ( error => { dispatch(fetchClientFailed()); }); } };
4. 修改Clientsearch.js:修正dispatch绑定 + 搜索值状态管理
import React, { Component } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { fetchClient } from '../../store/actions/indexAction'; // 导入正确的异步action class SearchBar extends Component { // 用组件内部状态管理搜索值,不需要存到Redux(如果需要存可以再扩展) state = { searchValue: '' } handleChange = (e) => { const value = e.target.value; this.setState({ searchValue: value }); this.props.fetchClient(value); // 调用异步action发起请求 } render() { return ( <input className='form-control' placeholder='Search Client' onChange={this.handleChange} value={this.state.searchValue} /> ); } } // 只需要绑定dispatch,不需要从Redux取状态,所以mapStateToProps传null const mapDispatchToProps = dispatch => { return bindActionCreators({fetchClient}, dispatch); // 绑定正确的action } export default connect(null, mapDispatchToProps)(SearchBar);
关键总结
- 初始化空数组是关键:给Redux状态里的
locations初始化为空数组,确保组件渲染时map不会因为undefined报错 - 状态流转要闭环:异步请求返回的数据,必须通过action传递给reducer,reducer更新state后,组件才能通过
mapStateToProps拿到最新值 - 细节别忽略:拼写错误、变量名单复数这类小问题,很容易导致莫名其妙的错误,写代码时多留意
内容的提问来源于stack exchange,提问作者Jeff Winkler
相关产品推荐
相关产品推荐

