如何用Immutable.js在React Redux中正确更新嵌套状态避免重复?
我来帮你梳理下怎么用Immutable.js解决这个Redux状态更新的问题,刚好你遇到的嵌套JSON状态和重复对象的痛点,Immutable.js就是专门用来处理这类场景的!
1. 先把整个Redux State转为Immutable数据结构
首先你需要把从MongoDB拿到的原始JSON状态,转换成Immutable的Map和List。Immutable会帮你自动维护状态的不可变性,避免手动修改原数据导致的问题。
你可以用fromJS方法直接把整个JS对象递归转成Immutable结构,数组会变成List,对象会变成Map:
import { fromJS } from 'immutable'; // 从MongoDB获取的原始状态片段 const initialRawState = { shops: [ { "shopId": "5a0c67e9fd3eb67969316cff", "picture": "http://placehold.it/150x150", "name": "Zipak", ... }, { "shopId": "5a0c6b55fd3eb67969316d9d", "picture": "http://placehold.it/150x150", "name": "Genekom", ... } ] }; // 转换为Immutable结构的初始状态 const initialState = fromJS(initialRawState);
2. Actions部分基本不需要改动
Actions的职责只是传递触发更新的关键信息,不需要处理Immutable结构。比如点赞的action还是正常传递shopId即可:
export const likeShop = (shopId) => ({ type: 'LIKE_SHOP', payload: { shopId } });
3. Reducer里用Immutable API做纯更新(核心解决重复问题)
你之前出现重复对象的问题,本质是因为直接修改了原对象,或者没有正确替换数组中的元素。Immutable的所有操作都是返回全新的Immutable对象,不会修改原数据,完美契合Redux的不可变状态要求。
处理点赞的reducer逻辑可以这么写:
import { fromJS } from 'immutable'; const shopReducer = (state = initialState, action) => { switch(action.type) { case 'LIKE_SHOP': { const { shopId } = action.payload; // 找到目标shop在List中的索引 const targetShopIndex = state.get('shops').findIndex(shop => shop.get('shopId') === shopId); if (targetShopIndex === -1) return state; // 没找到对应shop,直接返回原状态 // 用setIn方法更新嵌套路径的属性,返回全新状态 return state.setIn( ['shops', targetShopIndex, 'liked'], // 路径:shops数组 -> 目标索引 -> liked属性 true ); } default: return state; } };
这里的setIn方法会沿着你指定的嵌套路径更新属性,并且返回一个全新的Immutable状态,完全不会污染原有的shops列表或shop对象,自然就不会出现新旧对象重复的问题。
如果之后需要取消点赞,只需要把true改成false,或者用deleteIn移除这个属性:
// 取消点赞,移除liked属性 return state.deleteIn(['shops', targetShopIndex, 'liked']);
4. 组件中使用Immutable状态的小技巧
在组件里从Redux获取状态后,如果你需要把Immutable对象转回JS对象(比如渲染时用),可以用toJS()方法,但注意不要频繁调用——因为toJS()会深拷贝整个结构,可能影响性能。更推荐直接用Immutable的get、getIn方法获取属性:
// 获取单个shop的名称 const shopName = shop.get('name'); // 获取嵌套的地理位置坐标 const coordinates = shop.getIn(['location', 'coordinates']);
这样一套流程走下来,你的Redux状态就完全符合不可变性要求,再也不会出现更新时对象重复的问题了!
内容的提问来源于stack exchange,提问作者devio

