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
相关文章,请点击苏南大叔的博客:
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。