React自行实现Redux时遭遇Uncaught TypeError错误求助
问题:Redux结合API获取数据时出现
Uncaught TypeError: Cannot read properties of null (reading 'useState')错误 错误堆栈信息
Uncaught TypeError: Cannot read properties of null (reading 'useState') at useState (react.development.js:1622:1) at ApiReducer (ApiReducer.js:6:1) at redux.js:468:1 at Array.forEach (<anonymous>) at assertReducerShape (redux.js:466:1) at combineReducers (redux.js:531:1) at ./src/components/redux/reducers/CombineReducer.js (CombineReducer.js:4:1) at options.factory (react refresh:6:1) at __webpack_require__ (bootstrap:24:1) at fn (hot module replacement:62:1)
错误原因
- Reducer非法使用React Hooks:Redux的Reducer必须是纯函数,仅能接收
state和action参数并返回新状态,不能调用useState等React Hooks。Hooks仅允许在React组件或自定义Hooks中执行,Reducer运行时不在React组件渲染周期内,无法找到对应上下文,因此触发useState读取null的错误。 - Reducer包含异步操作:Reducer内直接发起
axios.get异步请求,违反了Redux Reducer的纯函数规则(不能有副作用、异步逻辑)。 - 状态处理逻辑错误:试图直接返回异步请求的Promise对象作为状态,不符合Redux通过action更新状态的工作流程。
解决方案
1. 重构Reducer(ApiReducer.js)
移除Hooks和异步逻辑,仅处理同步状态更新:
// ApiReducer.js const initialState = { data: null, error: null, loading: false }; const ApiReducer = (state = initialState, action) => { switch (action.type) { case 'FETCH_START': return { ...state, loading: true, error: null }; case 'FETCH_SUCCESS': return { ...state, loading: false, data: action.payload }; case 'FETCH_ERROR': return { ...state, loading: false, error: action.payload }; default: return state; } }; export default ApiReducer;
2. 重构Action(action.js)
将异步请求移至thunk action中,请求完成后dispatch对应action更新状态:
// action.js import axios from "axios"; export const tesla = () => { return async (dispatch) => { dispatch({ type: 'FETCH_START' }); try { const response = await axios.get("https://newsapi.org/v2/everything?q=tesla&from=2022-07-17&sortBy=publishedAt&apiKey=b1e1719ca0724ff9bd9dab0645884e9a"); dispatch({ type: 'FETCH_SUCCESS', payload: response.data }); } catch (error) { dispatch({ type: 'FETCH_ERROR', payload: error.message }); } }; }; export const business = () => { return async (dispatch) => { dispatch({ type: 'FETCH_START' }); try { const response = await axios.get("https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=b1e1719ca0724ff9bd9dab0645884e9c"); dispatch({ type: 'FETCH_SUCCESS', payload: response.data }); } catch (error) { dispatch({ type: 'FETCH_ERROR', payload: error.message }); } }; };
3. 修正组件逻辑(Rexercise.js)
移除组件内的useState,直接通过useSelector获取Redux状态:
// Rexercise.js import React from "react"; import { useSelector, useDispatch } from 'react-redux'; import { actions } from "./redux"; const Rexercise = () => { const { data, error, loading } = useSelector(state => state.value); const dispatch = useDispatch(); const Tesla = () => { dispatch(actions.tesla()); }; const Business = () => { dispatch(actions.business()); }; return ( <div className="container"> <h1>Hello World</h1> <button className="btn btn-primary" onClick={Tesla}>Tesla</button> <button className="mx-2 btn btn-success" onClick={Business}>Business</button> {loading && <div>加载中...</div>} {error && <div className="text-danger">请求错误:{error}</div>} {data && ( <div className="article text-primary"> <h3>新闻总数:{data.totalResults}</h3> <ul> {data.articles.slice(0, 5).map((article, index) => ( <li key={index}>{article.title}</li> ))} </ul> </div> )} </div> ); }; export default Rexercise;
4. 简化Store配置(可选)
Redux Toolkit的configureStore默认已包含redux-thunk middleware,无需手动添加,同时修正拼写错误:
// Store.js import { configureStore } from "@reduxjs/toolkit"; import reducers from "../reducers/CombineReducer"; const Store = configureStore({ reducer: reducers, preloadedState: {} }); export default Store;
内容的提问来源于stack exchange,提问作者piyush shende
相关产品推荐
相关产品推荐

