redux-toolkit api学习
createEntityAdapter
官方描述: A function that generates a set of prebuilt reducers and selectors for performing CRUD operations on a normalized state structure containing instances of a particular type of data object. 顺便需要了解一下什么是normalized state structure
在createAsyncThunk方法调用时,经常会看见搭配这个方法。用于格式化数据(normalized 标准化),会讲实例型的json数据格式化成扁平的数据结构,常用嵌套深的json数据标准化 参考normalizr库
例如从一篇博客数据接口中获取到的数据格式大致如下
[
{
"id": "123", - 文章id
"author": { - 作者信息
"id": "1", - 用户id
"name": "小明" - 用户名称
},
"title": "文章", - 文章标题
"comments": [ - 评论部分
{
"id": "324", - 评论id
"commenter": { - 评论用户信息
"id": "2", - 用户id
"name": "小王" - 用户名称
}
},
{
"id": "325", - 评论id
"commenter": { - 评论用户信息
"id": "1", - 用户id
"name": "小明" - 用户名称
}
},
]
}
]
上面的数据结构中其实有很多相同的数据类型比如 文章作者的信息 与 评论用户的信息 都是包含了用户的id和姓名,这些相同的数据类型其实都可以放在一个数组中,通过唯一id编号, 这样还有一个好处,比如用户评论中,其实小明的个人在作者信息与评论信息中是共享的,所以标准化后可以减少同样数据的重复,减少数据体积(0.0哈哈单个属性也许不明显,但是想想头像图片呢,只需要缓存一次即可)
格式化结果
{
result: "123",
entities: {
"articles": {
"123": {
id: "123",
author: "1",
title: "My awesome blog post",
comments: [ "324", "325" ]
}
},
"users": {
"1": { "id": "1", "name": "小明" },
"2": { "id": "2", "name": "小王" }
},
"comments": {
"324": { id: "324", "commenter": "2" },
"325": { id: "325", "commenter": "1" },
}
}
}
使用教程:
- 创建
createEntityAdapter这个方法可以接受一个带有两个可选属性的对象,两个key值分别是selectId与sortComparer
selectId: 传递一个函数,这个函数的参数是一个具体对象,从这个对象中选取一个id作为返回值, 如果不提供这个属性,函数默认会返回对象属性上的id,例如 entity => entity.id ,如果这个对象的唯一id不直接存在于这个对象属性上而是在对象子属性上,比如 entity.item.id,则必须提供一个函数返回一个选中的id。sortComparer: 传递一个函数,接受两个参数,根据返回对所有的对象进行指定排序,与数据的sort 差不多
const booksAdapter = createEntityAdapter<Book>({
// Assume IDs are stored in a field other than `book.id`
selectId: book => book.bookId,
// Keep the "all IDs" array sorted based on book titles
sortComparer: (a, b) => a.title.localeCompare(b.title)
})
-
enity adapter --- 生成后的对象 注意是一个 js object而非 class EntityAdapter 对象上包含了很多属性,分别是:
-
CURD functions 增删改
- addOne 添加一个enity
- addMany 添加多个enity,传递一个数组,数组中的元素要符合enity的对象结构
- setAll 传递一个enity数组,数组中的元素要符合enity的对象结构,将当前已经存在的enity内的所有数据替换成传递的
- removeOne 删除一个
- removeMany 删除多个
- removeAll 删除所有
- updateOne 更新一个
- updateMany 更新多个
- upsertOne 接受一个entity 如果存在则更新 不存在则添加
- upsertMany 接受多个entity 如果存在则更新 不存在则添加
-
getInitialState 初始化state 用于放在redux中,具体实现暂时未研究
- getInitialState 获取enity, 并且一个接受一个可选对象参数这个属性将添加到返回值上
-
Selector Functions 相当于查
- getSelectors()
- selectIds 获取ID数组
- selectEntities 获取实例对象,类似表
- selectAll 返回一个所有实例对象的数组 但是属性都是用id展示
- selectTotal 返回实例总数
- selectById 通过id查询实例
-
// 两种使用方式 其实就是指定了不同的作用域 一个是全局这种类型对象的一个是某一具体对象
const store = configureStore({
reducer: {
books: booksReducer
}
})
const simpleSelectors = booksAdapter.getSelectors()
const globalizedSelectors = booksAdapter.getSelectors(state => state.books)
// Need to manually pass the correct entity state object in to this selector
const bookIds = simpleSelectors.selectIds(store.getState().books)
// This selector already knows how to find the books entity state
const allBooks = globalizedSelectors.selectAll(store.getState())
- Note
- 多次调用
updateMany,对相同ID最后一次更新的结果将覆盖前面的结果 updateOne和updateMany这两个函数, 修改一个已存在实例的ID 更换为 第二个实例中匹配的ID 遇到第一个完全匹配的ID后就会直接替换
import {createEntityAdapter, createSlice, configureStore} from '@reduxjs/toolkit';
// 创建一个book对象 通过title排序
const booksAdapter = createEntityAdapter({
sortComparer: (a, b) => a.title.localeCompare(b.title)
})
const booksSlice = createSlice({
name: 'books',
//给book对象绑定一个属性
initialState: booksAdapter.getInitialState({
loading: 'idle'
}),
reducers: {
//添加一本
bookAdded: booksAdapter.addOne,
//修改加载庄状态
booksLoading(state, action) {
if (state.loading === 'idle') {
state.loading = 'pending'
}
},
//book数据置换
booksReceived(state, action) {
if (state.loading === 'pending') {
booksAdapter.setAll(state, action.payload)
state.loading = 'idle'
}
},
//更新其中一本书的信息
bookUpdated: booksAdapter.updateOne
}
})
const {
bookAdded,
booksLoading,
booksReceived,
bookUpdated
} = booksSlice.actions
const store = configureStore({
reducer: {
books: booksSlice.reducer
}
})
// 检查初始状态
console.log(store.getState().books)
// {ids: [], entities: {}, loading: 'idle' }
// 返回一个selector函数作用域 方便后面调用
const booksSelectors = booksAdapter.getSelectors(state => state.books)
store.dispatch(bookAdded({ id: 'a', title: 'First' }))
console.log(store.getState().books)
// {ids: ["a"], entities: {a: {id: "a", title: "First"}}, loading: 'idle' }
store.dispatch(
booksReceived([
{ id: 'b', title: 'Book 3' },
{ id: 'c', title: 'Book 2' }
])
)
console.log(booksSelectors.selectIds(store.getState()))
// "a" was removed due to the `setAll()` call
// Since they're sorted by title, "Book 2" comes before "Book 3"
// ["c", "b"]
console.log(booksSelectors.selectAll(store.getState()))
// All book entries in sorted order
// [{id: "c", title: "Book 2"}, {id: "b", title: "Book 3"}]