React中无法渲染Redux状态数据 报读取undefined的id属性错误
问题场景
开发简易论坛应用,需要渲染论坛帖子回复相关信息,使用Redux管理接口请求状态,代码运行时出现渲染报错。
原有实现代码
Redux Action 与 Reducer
export const getRoomContext = (id) => async (dispatch) =>{ try{ dispatch({type: 'GET_ROOM_MESSAGES1_REQUEST'}) const { data } = await axios.get(`https://localhost:5001/api/Forum/posts/${id}`) dispatch({ type: GET_ROOM_MESSAGES_SUCCESS, payload: data }) } catch(error){ dispatch({ type: GET_ROOM_MESSAGES_FAIL, payload: error.response && error.response.data.detail ? error.response.data.detail : error.message }) }} export const getRoomContextReducer = (state = {}, action) =>{ switch(action.type){ case 'GET_ROOM_MESSAGES1_REQUEST': return {loading: true, getRoominfo: []} case GET_ROOM_MESSAGES_SUCCESS: return {loading: false, getRoominfo: action.payload} case GET_ROOM_MESSAGES_FAIL: return {loading: false, error: action.payload} default: return state }}
页面渲染组件
export default function PostScreen2() { const {id} = useParams() const dispatch = useDispatch() const downMessage = useSelector(state => state.downMessage) const {error, loading, getRoominfo} = downMessage useEffect(() =>{ dispatch(getRoomContext(id)) }, [dispatch, id]) return( <div>{getRoominfo.id}</div> )
报错现象
- 渲染
{getRoominfo.id}时,Chrome Redux开发者扩展中的状态消失,页面完全空白,控制台抛出错误:Uncaught TypeError: Cannot read properties of undefined (reading 'id') - 替换为普通字符串渲染时,页面可正常运行,状态正常展示无报错
- 静态字符串渲染正常后改回渲染
{getRoominfo.id}可临时显示内容,但刷新页面后会再次出现相同错误
问题根因
- Reducer初始状态设置为空对象
{},组件首次挂载时,接口请求还未触发/返回,从store中解构出的getRoominfo值为undefined,直接读取undefined.id触发类型错误 - 请求触发阶段Reducer将
getRoominfo设置为空数组[],数组类型不存在id属性,即使进入请求加载状态,直接读取id依然会报错 - Action Type存在混用风险:请求阶段的type直接写字符串字面量
'GET_ROOM_MESSAGES1_REQUEST',其余两个状态使用常量定义,若常量值与字面量不匹配会导致状态更新逻辑失效 - 组件未做加载状态兜底,接口异步请求未完成时就尝试读取返回数据的属性
修复方案
- 统一Reducer初始状态,为所有字段设置明确的初始值,请求阶段保留原有数据或设置为空对象(匹配接口返回的帖子对象结构,不要用空数组)
- 统一Action Type定义,全部使用常量避免拼写错误
- 组件渲染时增加加载、错误状态判断,确认数据存在后再渲染对应字段,可搭配可选链操作符做额外兜底
修复后代码
Reducer部分
// 统一定义Action Type常量,避免拼写不匹配 const GET_ROOM_MESSAGES1_REQUEST = 'GET_ROOM_MESSAGES1_REQUEST' const GET_ROOM_MESSAGES_SUCCESS = 'GET_ROOM_MESSAGES_SUCCESS' const GET_ROOM_MESSAGES_FAIL = 'GET_ROOM_MESSAGES_FAIL' export const getRoomContextReducer = (state = { loading: false, getRoominfo: {}, error: null }, action) =>{ switch(action.type){ case GET_ROOM_MESSAGES1_REQUEST: return { ...state, loading: true } case GET_ROOM_MESSAGES_SUCCESS: return { loading: false, getRoominfo: action.payload, error: null } case GET_ROOM_MESSAGES_FAIL: return { loading: false, error: action.payload, getRoominfo: {} } default: return state } }
组件渲染部分
export default function PostScreen2() { const {id} = useParams() const dispatch = useDispatch() const downMessage = useSelector(state => state.downMessage) const {error, loading, getRoominfo} = downMessage useEffect(() =>{ dispatch(getRoomContext(id)) }, [dispatch, id]) return( <div> {loading && <div>帖子内容加载中...</div>} {error && <div>加载失败:{error}</div>} {!loading && !error && getRoominfo && ( <div>{getRoominfo?.id}</div> )} </div> ) }
内容的提问来源于stack exchange,提问作者Hubert Kobos
相关产品推荐
相关产品推荐

