我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

redux代码中,定义好一个实体适配器EntityAdapter之后,那么,这个实体适配器具体表述的实体是什么?又有哪些天生的操作呢?这就是本文要讨论的问题:实体适配器的16个可用的操作(reducer)。

苏南大叔:redux教程,实体适配器EntityAdapter有哪些可用操作? - 实体适配器的可用操作
redux教程,实体适配器EntityAdapter有哪些可用操作?(图4-1)

大家好,苏南大叔又来普及redux文章内容了,本文描述实体适配器的16个可用操作。测试环境:win10node@16.14.2webpack@5.74.0webpack-cli@4.10.0webpack-dev-server@4.9.3reduxjs/toolkit@1.8.3chrome@103.0.5060.53redux-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,进而生成可以直接dispacthaction的。

console.log(personAdapter);

苏南大叔:redux教程,实体适配器EntityAdapter有哪些可用操作? - 查看可用操作
redux教程,实体适配器EntityAdapter有哪些可用操作?(图4-2)

官方文档里面是这么写的:

  • 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;

苏南大叔:redux教程,实体适配器EntityAdapter有哪些可用操作? - reducer生效途径
redux教程,实体适配器EntityAdapter有哪些可用操作?(图4-3)

调用方式

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来决定执行是否插入数据。上述例子中,因为ida的数据属于二次插入,所以并不会真正被执行。执行结果:

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不存在,则添加整条数据。
根据苏南大叔的合理推测,这里的setManysetOne是马甲函数,真身是下面将要讲到的upsertManyupsertOne
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'}

操作对比

这里对setupdateupsert这三个迷之操作做个对比。

名称操作参数格式初始化已存在的数据不存在的数据
setsetAll,setMany,setOne-setAll初始化整条更新直接添加
upsertupsertMany,upsertOne--整条更新直接添加
updateupdateMany,updateOnechanges-更新字段不做修改

那么,关于setManyupsertMany,明显就是一样的操作嘛。setOneupsertOne,也明显是一样的操作。那么,upsertManyupsertOne的存在意义是啥?

测试代码:

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}

看起来,运行结果确实是一致的。对此,官方直接采取了回避态度,文档里面根本就没有setManysetOne啥事...

苏南大叔:redux教程,实体适配器EntityAdapter有哪些可用操作? - 函数对比结果
redux教程,实体适配器EntityAdapter有哪些可用操作?(图4-4)

相关文章

综述

所以这16个操作里面,

名称说明
sortComparer、selectId用于初始化适配器设置
getInitialState初始化state
getSelector系列读取state 【r】
其余各种add/set/update对state的设置【cud】

更多redux相关文章,请点击苏南大叔的博客:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   redux