React Redux双Dispatch问题:同时触发请求状态被覆盖
核心问题:Reducer未保留原有状态
嘿,我一眼就发现问题出在你的Redux reducer里!你现在处理每个action时,直接返回了一个全新的对象,但完全没把之前状态里的其他字段合并进来。比如触发FETCH_DESIGN_CAT时,你只返回了designcat和loading,原来的foodcat、db这些字段直接被丢弃了;等第二个FETCH_FOOD_CAT触发时,又只返回了food(这里还有个字段名写错的小bug)和loading,自然就把第一个action更新的状态给覆盖了。
另外还有个细节错误:FETCH_FOOD_CAT里你把数据存在了food字段,但初始状态和mapStateToProps里用的都是foodcat,这会导致你根本拿不到食物分类的数据哦。
修复步骤
1. 修正Reducer的状态合并逻辑
修改你的reducer,每次返回新状态时,先用扩展运算符...state复制原有状态的所有字段,再覆盖需要更新的部分,同时修正字段名错误:
const initialState = { db: [], loading: true, designcat: [], foodcat: [], } export default (state = initialState, action) => { switch (action.type) { case FETCH_DESIGN_CAT: return { ...state, // 保留原有所有状态字段 designcat: action.payload, loading: false, } case FETCH_FOOD_CAT: return { ...state, // 保留原有所有状态字段 foodcat: action.payload, // 把food改成foodcat,和初始状态对应 loading: false, } default: // 别忘了default分支,返回原有状态避免异常 return state } }
2. 修复服务器端路由的路径问题
你服务器端的两个GET路由都是"/",这肯定不对!虽然你说单个请求能工作,但应该是粘贴错误——毕竟你的action里请求的是"/api/designcategories"和"/api/foodcategories",所以服务器端的路由路径要对应修改:
// 设计分类路由 router.get("/designcategories", (req, res) => { let sql = "SELECT * FROM design_categories"; let query = connection.query(sql, (err, results) => { if (err) throw err; res.header("Access-Control-Allow-Origin", "*"); res.send(results); }); }); // 食物分类路由 router.get("/foodcategories", (req, res) => { let sql = "SELECT * FROM food_categories"; let query = connection.query(sql, (err, results) => { if (err) throw err; res.header("Access-Control-Allow-Origin", "*"); res.send(results); }); });
3. (可选)优化action中的异步逻辑
你的action里await和.then()混用了,简化一下让代码更清晰:
export const fetchDesignCat = () => { setLoading() return async dispatch => { try { const response = await axios.get("http://localhost:5000/api/designcategories"); dispatch({ type: FETCH_DESIGN_CAT, payload: response.data }); } catch (error) { console.log("await error", error); } } } export const fetchFoodCat = () => { setLoading() return async dispatch => { try { const response = await axios.get("http://localhost:5000/api/foodcategories"); dispatch({ type: FETCH_FOOD_CAT, payload: response.data }); } catch (error) { console.log("await error", error); } } }
为什么这样能解决问题?
Redux的reducer是纯函数,每次更新状态都要返回新对象,但必须保留不需要修改的原有字段。用...state扩展运算符就能把之前状态里的所有属性复制到新对象里,再修改需要更新的字段,这样就不会覆盖之前action已经更新的内容了。
内容的提问来源于stack exchange,提问作者Marco Disco

