React18升级react-redux至v8后报读取null的data属性错误
React 18 升级 React-Redux 8 后空指针报错修复
问题表现
将React升级至18版本、react-redux升级至8.0.2版本后,项目运行时Requests.js文件抛出未捕获类型错误:
Uncaught TypeError: Cannot read properties of null (reading 'data')
登录流程中接口响应的返回数据无法正常存入Redux store。
关联代码片段
Redux Store 配置代码
import { createStore, applyMiddleware } from "redux"; import { persistStore, persistReducer } from "redux-persist"; import storage from "redux-persist/lib/storage"; import thunk from "redux-thunk"; import reducer from "../reducers/combiner"; const persistConfig = { key: "app", storage, }; const persistedReducer = persistReducer(persistConfig, reducer); const store = createStore( persistedReducer, applyMiddleware(thunk) ); const persistor = persistStore(store); export { store, persistor };
项目依赖配置
"dependencies": { "@reduxjs/toolkit": "^1.8.2", "bootstrap": "^5.1.3", "crypto-js": "^4.1.1", "js-base64": "^3.7.2", "leaflet": "^1.8.0", "leaflet-routing-machine": "^3.2.12", "node-sass": "^7.0.1", "react": "^18.1.0", "react-dom": "^18.1.0", "react-leaflet": "^4.0.0", "react-redux": "^8.0.2", "react-router-dom": "^6.3.0", "react-scripts": "^5.0.1", "redux": "^4.2.0", "redux-persist": "^6.0.0", "redux-thunk": "^2.4.1", "reselect": "^4.1.5", "typescript": "^4.7.2", "uuid": "^3.4.0" }
Store 数据消费代码
useEffect(() => { const authKey = localStorage.getItem("auth"); if (authKey === null) { navigate("/login"); } else { if (window.innerWidth <= 767) { window.scrollTo({ top: 600, behavior: "smooth" }); } if (userInfo !== null && userInfo.data && userInfo.data.order_id !== 0) { navigate("/driver"); } else if (latLon.lat === 35.6892 && latLon.lng === 51.389) { dispatch(sendToMapIR(latLon.lat, latLon.lng)); dispatch(deleteRestAddress()); } } }, []);
Redux Action 实现代码
export const newRequest = ( userId, lat, lng, address, pickedItems, total, checked, restAddress ) => { return async dispatch => { dispatch({ type: NEW_REQUSET_WAITING }); const result = await fetchNewRequest( userId, lat, lng, address, pickedItems, total, checked, restAddress ); if (result.success) { return dispatch({ type: NEW_REQUSET, payload: result.data }); } else { return dispatch({ type: NEW_REQUSET_FAILED }); } }; };
问题根因
- React-Redux 8.x 搭配 React 18 使用时,对store注入的时序要求更严格,如果根组件没有按正确顺序包裹
PersistGate,redux-persist的持久化数据还未完成注水(rehydrate),业务组件就已经完成挂载并读取store值,此时store中初始化的userInfo为null,直接读取.data属性就会触发空指针错误。 - Redux reducer中
userInfo的初始值设置为null,没有做默认值兜底,在状态更新的渲染间隙会出现短暂的null值读取场景。 - useEffect传入空依赖数组,React 18严格模式下effect会重复执行,首次执行时store数据还未完成初始化,无法拿到正确的状态值。
修复方案
- 修正根组件的Provider与PersistGate包裹顺序,必须先注入Redux store,再挂载持久化门限组件,确保持久化数据加载完成后再渲染业务组件,参考写法:
import { Provider } from 'react-redux' import { PersistGate } from 'redux-persist/integration/react' import { store, persistor } from './store' const root = ReactDOM.createRoot(document.getElementById('root')) root.render( <Provider store={store}> <PersistGate loading={null} persistor={persistor}> <App /> </PersistGate> </Provider> )
- 给Redux初始状态中的
userInfo设置安全默认值,避免null值导致的属性读取报错,例如将reducer中userInfo初始值改为{ data: { order_id: 0 } };也可以在读取属性时使用可选链做防御性编程,将判断逻辑修改为:
if (userInfo?.data?.order_id !== 0) { navigate("/driver") }
- 给useEffect补充完整依赖项,移除空依赖数组,将逻辑中用到的
userInfo、latLon、navigate、dispatch全部加入依赖列表,确保状态更新后effect逻辑能拿到最新值正确执行。 - 项目已经安装
@reduxjs/toolkit的前提下,建议后续迁移到RTK提供的configureStore方法创建store,无需手动配置thunk中间件,可规避多数旧版createStore写法的兼容问题。
内容的提问来源于stack exchange,提问作者mina houshmand
相关产品推荐
相关产品推荐

