redux教程,实体适配器EntityAdapter有哪些可用操作?
发布于 作者:苏南大叔 来源:程序如此灵动~
在redux代码中,定义好一个实体适配器EntityAdapter之后,那么,这个实体适配器具体表述的实体是什么?又有哪些天生的操作呢?这就是本文要讨论的问题:实体适配器的16个可用的操作(reducer)。

大家好,苏南大叔又来普及redux文章内容了,本文描述实体适配器的16个可用操作。测试环境:win10,node@16.14.2,webpack@5.74.0,webpack-cli@4.10.0,webpack-dev-server@4.9.3,reduxjs/toolkit@1.8.3,chrome@103.0.5060.53,redux-devtools-extension@3.0.11。
基本代码
上一篇文章中,介绍了:一个实体适配器是如何初始化state,通过slice来生成store的。参考:
测试代码:
import {createEntityAdapter,createSlice,configureStore} from '@reduxjs/toolkit';
const personAdapter = createEntityAdapter({
sortComparer: (a, b) => a.name.localeCompare(b.name),
// selectId: (person) => person.id,
});
const initState = personAdapter.getInitialState({loading: 'idle'});
const personSlice = createSlice({
name: 'person',
initialState: initState,
reducers: {
//...
}
});如果打印一下这个生成的EntityAdapter的话,就可以看到一系列可以操作的函数。这些之中除了已知的三个之外,其它的都是可以作为reducer直接传入slice,进而生成可以直接dispacth的action的。
console.log(personAdapter);
官方文档里面是这么写的:
- getInitialState: returns an object that looks like { ids: [], entities: {} }, for storing a normalized state of items along with an array of all item IDs
- getSelectors: generates a standard set of selector functions
- addOne / addMany: add new items to the state
- upsertOne / upsertMany: add new items or update existing ones
- updateOne / updateMany: update existing items by supplying partial values
- removeOne / removeMany: remove items based on IDs
- setAll: replace all existing items
参考文章:
已知的属性
sortComparer(),设置排序标准。selectId(),明确说明数据的唯一性标准。getInitialState(),获得初始化状态。用于传递到slice。getSelector(),读取state,这个后续单开文章说明。
const personAdapter = createEntityAdapter({
sortComparer: (a, b) => a.name.localeCompare(b.name),
// selectId: (person) => person.id,
});
const initState = personAdapter.getInitialState({
loading: 'idle'
});其余CRUD类属性
这些实体适配器的属性,对于redux来说是reducer。真正的可以使用的话,还是需要变成action,才能被dispatch。
const personSlice = createSlice({
name: 'person',
initialState: initState,
reducers: {
personAddMany:personAdapter.addMany,
personAddOne:personAdapter.addOne,
personRemoveAll:personAdapter.removeAll,
personRemoveMany:personAdapter.removeMany,
personRemoveOne:personAdapter.removeOne,
personSetAll:personAdapter.setAll,
personSetMany:personAdapter.setMany,
personSetOne:personAdapter.setOne,
personUpdateMany:personAdapter.updateMany,
personUpdateOne:personAdapter.updateOne,
personUpsertMany:personAdapter.upsertMany,
personUpsertOne:personAdapter.upsertOne,
personSetAll2(state, action) {
personAdapter.setAll(state, action.payload);
state.loading = 'idle';
}
}
});
const store = configureStore({
reducer: {
person: personSlice.reducer,
}
});
const {
personAddMany,personAddOne,personRemoveAll,personRemoveMany,personRemoveOne,
personelectId,personSetAll,personSetMany,personSetOne,personUpdateMany,
personUpdateOne,personUpsertMany,personUpsertOne
} = personSlice.actions;
调用方式
reducer内部相互调用的方式是:
//...
personSetAll2(state, action) {
personAdapter.setAll(state, action.payload)
state.loading = 'idle'
}
//...外部通过actions调用的方式范例是:
store.dispatch(personAddOne({ id: 'a', name: '苏' }))添加数据:addMany,addOne
添加数据,添加多条数据和添加一条数据,区别就在于实参是个对象数组还是一条普通的对象。
测试例子:
store.dispatch(personAddMany([
{ id: 'a', name: '苏1' },
{ id: 'b', name: '苏2' },
]));
store.dispatch(personAddOne({ id: 'a', name: '苏111' })) //不会被add
store.dispatch(personAddOne({ id: 'c', name: '苏666' })) //会被add
console.log(store.getState().person)这里要插入的数据,根据selectId来决定执行是否插入数据。上述例子中,因为id为a的数据属于二次插入,所以并不会真正被执行。执行结果:
a: {id: 'a', name: '苏1'}
b: {id: 'b', name: '苏2'}
c: {id: 'c', name: '苏666'}删除数据:removeAll,removeMany,removeOne
删除数据,根据selectId的设定值id来删除数据。
store.dispatch(personRemoveOne("b")); //b被删除
store.dispatch(personRemoveMany(["a","d"])); //存在就删除,a被删除
store.dispatch(personRemoveAll()); //最后一个c被删除
console.log(store.getState().person)这个操作的结果是:刚刚被添加的几条数据,最终全部被删除,没有数据。
设置数据:setAll,setMany,setOne
设置数据,根据selectId的设定值id来【添加或更新】数据。其中的setAll操作,就是重新初始化所有数据,已有数据全部不保留。
id存在,更新整条数据。id不存在,则添加整条数据。
根据苏南大叔的合理推测,这里的setMany和setOne是马甲函数,真身是下面将要讲到的upsertMany和upsertOne。
store.dispatch(personAddOne({ id: 'cc', name: '苏6' })); //马上将被覆盖,不再存在
store.dispatch(personSetAll([ // 整体重新初始化了
{ id: 'a', name: '苏1' },
{ id: 'b', name: '苏2' },
{ id: 'c', name: '苏3' },
{ id: 'd', name: '苏4' },
{ id: 'e', name: '苏5' }
]));
store.dispatch(personSetMany([
{ id: 'a', name: '苏11' }, //已有数据被更新
{ id: 'bb', name: '苏22' }, //新的数据被添加
]));
store.dispatch(personSetOne(
{ id: 'c', name: '苏666' } //否则自动修改
));
console.log(store.getState().person);更新数据:updateMany,updateOne
数据更新,根据指定的id更新指定的字段。指定的id不存在的话,则不做任何操作。
这个格式比较特殊,存在着一个changes字段。store.dispatch(personUpdateMany([
{ id: 'a', changes: { name: '苏111' } } ,
{ id: 'b', changes: { name: '苏222','ok':true } } ,
{ id: 'ccc', changes: { name: '苏666'} } , //不存在的ccc,没反应
]));
store.dispatch(personUpdateOne(
{ id: 'd', changes: { name: '苏大哥' } }
));
console.log(store.getState().person);运行结果是:
a: {id: 'a', name: '苏111'}
b: {id: 'b', name: '苏222', ok: true}
bb: {id: 'bb', name: '苏22'}
c: {id: 'c', name: '苏666'}
d: {id: 'd', name: '苏大哥'}
e: {id: 'e', name: '苏5'}更新或插入数据:upsertMany,upsertOne
数据更新或插入,数据存在,整条改写。数据不存在,直接插入。
store.dispatch(personUpsertMany([
{ id: 'a', name: '苏1' },
{ id: 'b', name: '苏2' },
{ id: 'cc', name: '苏123' } //被添加进去了
]));
store.dispatch(personUpsertOne(
{ id: 'e', name: '苏555' }
));
console.log(store.getState().person);运行结果:
a: {id: 'a', name: '苏1'}
b: {id: 'b', name: '苏2', ok: true}
bb: {id: 'bb', name: '苏22'}
c: {id: 'c', name: '苏666'}
cc: {id: 'cc', name: '苏123'}
d: {id: 'd', name: '苏大哥'}
e: {id: 'e', name: '苏555'}操作对比
这里对set、update、upsert这三个迷之操作做个对比。
| 名称 | 操作 | 参数格式 | 初始化 | 已存在的数据 | 不存在的数据 |
|---|---|---|---|---|---|
| set | setAll,setMany,setOne | - | setAll初始化 | 整条更新 | 直接添加 |
| upsert | upsertMany,upsertOne | - | - | 整条更新 | 直接添加 |
| update | updateMany,updateOne | changes | - | 更新字段 | 不做修改 |
那么,关于setMany和upsertMany,明显就是一样的操作嘛。setOne和upsertOne,也明显是一样的操作。那么,upsertMany和upsertOne的存在意义是啥?
测试代码:
store.dispatch(personSetAll([ // 整体重新初始化了
{ id: 'a', name: '苏1' },
{ id: 'b', name: '苏2' },
]));
store.dispatch(personSetOne(
{ id: 'a', name: '苏11', ok:true }
));
store.dispatch(personUpsertOne(
{ id: 'b', name: '苏22', ok:true }
));
store.dispatch(personSetOne(
{ id: 'aa', name: '苏888', ok:true }
));
store.dispatch(personUpsertOne(
{ id: 'bb', name: '苏999', ok:true }
));
console.log(store.getState().person);运行结果:
a: {id: 'a', name: '苏11', ok: true}
b: {id: 'b', name: '苏22', ok: true}
aa: {id: 'aa', name: '苏888', ok: true}
bb: {id: 'bb', name: '苏999', ok: true}看起来,运行结果确实是一致的。对此,官方直接采取了回避态度,文档里面根本就没有setMany和setOne啥事...

相关文章
- https://newsn.net/say/redux-store.html
- https://newsn.net/say/redux-slice.html
- https://newsn.net/say/redux-slice-2.html
- https://newsn.net/say/redux-entity-adapter.html
综述
所以这16个操作里面,
| 名称 | 说明 |
|---|---|
| sortComparer、selectId | 用于初始化适配器设置 |
| getInitialState | 初始化state |
| getSelector系列 | 读取state 【r】 |
| 其余各种add/set/update | 对state的设置【cud】 |
更多redux相关文章,请点击苏南大叔的博客: