Redux - redux/toolkit

简化Redux使用步骤, 及所需要的声明文件.

解决:

  • 太多的样板文件、模版代码(action、reducer)
  • 复杂的配置 , 处理中间件
  • state 更新麻烦 , 自行处理返回新的对象
  • 自带默认配置,中间件:redux-thunk\redux-devtools-extension
安装
npm i @reduxjs/toolkit
示例
基本使用
configureStore

对比之前的代码,不再使用reduxcreateSotre\applyMiddleware方法 . 传参由redux/toolkit内部处理.

// import {createStore,applyMiddleware} from 'redux'
import {configureStore,getDefaultMiddleware} from '@reduxjs/toolkit'
import createSagaMiddleware from 'redux-saga'
// middlewares
import {logger} from './middlewares/logger'
// sagas 
import rootSaga from './sagas'
import reducer from './reducers'

const sagaMiddleware = createSagaMiddleware()
// init
// const store = createStore(reducer,applyMiddleware(sagaMiddleware,logger))
const store = configureStore({reducer,middleware:[sagaMiddleware,logger,...getDefaultMiddleware()]})
// 之后运行saga
sagaMiddleware.run(rootSaga)
export default store
createReducer

对比以前的使用,在处理action时,reducer需要返回一个新的拷贝的state,不能直接修改;现在可以了🤒

import {createReducer} from '@reduxjs/toolkit'

const INIT_STATE = {
    name:'admin',
    age:23,
    address:'江苏省 南京市'
}
/**
 * 
export function UserInfo(state=INIT_STATE,action) {
    const {type,data} = action
    switch(type){
        case 'updateName':
            return {
                ...state,
                name:data,
            }
        case 'updateAge':
            return {
                ...state,
                age:data,
            }
        default:
            return state
    }
}
*/
export const UserInfo = createReducer(INIT_STATE,{
    updateName:(state,action)=>{
        state.name = action.data
    },
    updateAge:(state,action)=>{
        state.age = action.data
    }
})

示例中action.data 系自定义格式,现在已习惯命名为payload , 在使用createAction 后只能使用action.payload 获取数据.

示例中为mapAction写法,还有一个builder=>builder.addCase() 写法,细节移步API

// builder.addCase() 写法
export const UserInfo = createReducer(INIT_STATE,builder=>{
    builder.addCase(updateName,(state,action)=>{
        state.name = action.payload
    })
    .addCase(updateAge.type,(state,action)=>{
        state.age = action.payload
    })
})
createAction

按照之前的编写应用 , 及时不使用这个创建action creator,程序依然正常运行.

使用之前的逻辑,自定义action creator :

// action creator
function updateName(name) {
    return {
        type:'updateName',
        data:name,
    }
}
// ...
// 视图更新数据的 触发的action
return (<div style={{border:'1px solid #fff'}}>
        <Input onChange={e=>setName(e.target.value)} onPressEnter={e=>dispatch(updateName(name))} />
    </div>)

使用createAction之后

import {createAction} from '@reduxjs/toolkit'
// action creator
// function updateName(name) {
//     return {
//         type:'updateName',
//         data:name,
//     }
// }
const updateName = createAction('updateName')

应用时, 拿不到数据 , 因为createAction默认的数据格式为payload , 改一下reducer中的数据取值 .

如果需要更改格式,则需要传递第二个参数callback

const updateName = createAction('updateName',(content)=>{
    // API 约定了格式,payload 可自定数据
    // 详细查看API部分
    return {
        payload:{
            data:content,
        },
        meta:{},
        error:{},
    }
})

createReducer一起使用

export const updateName = createAction('updateName')
export const updateAge = createAction('updateAge')

export const UserInfo = createReducer(INIT_STATE,{
    [updateName]:(state,action)=>{
        state.name = action.payload
    },
    [updateAge.type]:(state,action)=>{
        state.age = action.payload
    }
})

createAction 创建的可直接作为action type使用 , 它的.toString被改写,同.type

createSlice

此为高阶函数,由createActioncreateReducer 组合实现.通过配置的方式.内部处理调用.

import {createSlice} from '@reduxjs/toolkit'

const UserInfoModel = createSlice({
    name:"userInfo",
    initialState:INIT_STATE,
    reducers:{
        updateName(state,action){
            state.name = action.payload
        },
        updateAge(state,action){
            state.age = action.payload
        }
    }
})

// 获取reducer 、 action
const {actions,reducer} = UserInfoModel

// 导出action
export const {updateName,updateAge} = actions
// 导出reducer
export const UserInfo = reducer

按功能、模块划分 , 都只有一个文件 . 不需要写type 枚举定义

异步使用

内置了redux-thunk 处理异步 , 足够解决绝大部分的问题. 还有其他中间件比如:redux-sagaredux-observable

createAsyncThunk

异步请求处理三种状态的action :pending\fulfilled\rejected

这三种状态的action自动触发, 防止外部手动调用,则使用属性extraReducers , 则不会生成对外的的action creator .

// 异步请求数据
export const fetchUserInfo = createAsyncThunk('users/fetchUserInfo',async (userId,thunkAPI)=>{
    const res =await new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve({
                name:'hboot',
                age:26,
                address:'江苏省 南京市 栖霞区'
            })
        },5000)
    })
    return res
})

// createSlice
const UserInfoModel = createSlice({
    name:"userInfo",
    initialState:INIT_STATE,
    reducers:{
        updateName(state,action){
            state.name = action.payload
        },
        updateAge(state,action){
            state.age = action.payload
        }
    },
    extraReducers:{
        [fetchUserInfo.fulfilled]:(state,action)=>{
            return action.payload
        }
    }
})

数据管理

通常用来管理列表数据, 内置了常用的数据操作方法.

可以对比和之前示例代码中的不同之处.

/**
 * 获取文章数据列表
 */
// const INIT_STATE = {
//     id:'',
//     title:'', // 文章标题
//     tags:[],   // 分类
//     content:'',   // 文章内容
//     author:'', // 作者信息
//     createTime:'' , // 创建时间
//     status:'' , // 状态:0 失败 1 成功 2 开始请求
//     comments:[],
// }

// createEntityAdapter
const articleModel = createEntityAdapter({
    selectId:(article)=>article.id
}) 

const ArticleInfoModel = createSlice({
    name:"articleInfo",
    initialState:articleModel.getInitialState(),
    reducers:{
        getArticleSuccess(state,action){
            articleModel.setAll(state,action.payload)
        },
        clearArticleInfo(state){
            return articleModel.getInitialState()
        },
        updateArticleInfoById(state,action){
            articleModel.updateOne(state,action.payload)
        }
    }
})

const {actions,reducer} = ArticleInfoModel
// actions
export const {getArticleSuccess,clearArticleInfo,updateArticleInfoById} = actions
// reducer
export const ArticleInfo = reducer

则在UI组件获取数据时 , {ids,entities}

// ... 
const { ids, entities } = article;
    return ids.map((id) => {
      const item = entities[id];
      return (
        <>
          <p>
            {item.title} - {item.author}
          </p>
          <main>
            <div>
              {item.tags.map((tag) => (
                <Tag key={tag}>{tag}</Tag>
              ))}
            </div>
            <TextArea defaultValue={item.content} onPressEnter={(e) => updateArticleById({...item,content:e.target.value})} />
          </main>

          <footer>{item.createTime}</footer>
        </>
      );
    });
// ... 
API
Store 基础设置

configureStore({reducer:fn|{},middleware:fn|[],devTools:boolean|{},preloadedState:any,enhancers:[]|fn})

代替creatStore

参数:

  1. reducer 接受单个reducer 或者一个对象(内部调用了combineReducers()合并)
  2. middleware? 中间件配置,内部通过applyMiddleware;接受一个回调函数(getDefaultMiddleware)获取默认配置的中间件自定义设置;
  3. devTools? 是否开启redux浏览器扩展;接受对象配置默认的设置参数
  4. preloadedState? 初始化默认的state;如果是 rootReducer 是合并的,则需要对象键值映射.
  5. enhancers? 自定义增强redux store ;

getDefaultMiddleware

返回默认的中间件配置 ; 检测不可变数据变化和检测无法序列化的类型的数据

const middleware = [thunk, immutableStateInvariant, serializableStateInvariant]

生产环境下只有thunk

reducer&action

createReducer(initState,fn|Map Object)

创建reducer的实用函数 , 有两种使用方式处理reducer逻辑

  1. fn:(builder)=>builder.addCase(type,(state,action)=>)
  • addCase(string|actionCreator,fn) 精准匹配单个action处理; 先与其他两个方法调用.
  • addMatcher(matcher,fn) 模式匹配,处理匹配到的所有action
  • addDefaultCase(fn) 未匹配到的action处理;最后调用
  1. (initState,mapActions,mapMatchers,defaultCaseReducer)
  • mapActions:{[action type]:reducer} 处理单个action
  • mapMatchers:[{matcher,reducer}] 模糊匹配,处理所有匹配到的action
  • defaultCaseReducer:reducer 处理未匹配时的调用

createAction(type,prepareAction)

  • .type 返回当前的action type
  • .match(action) 用于匹配是否符合改action

创建action creator 函数, 该函数的.toString() 方法被改造返回type ;

  1. type:any 可以是任意类型(包括不可序列化数据类型)
  2. prepareAction(content:any) 可自定义action内容; 返回值为一个对象,包含:
    • payload 相当于action.payload 的内容,
    • meta 额外的action信息
    • error 异常信息

createSlice(name,initialState,reducers,extraReducers?)

  • .name
  • .reducer
  • .actions
  • .caseReducer

createActioncreateReducer 的高阶实现. 提供参数, 内部调用自动创建action type 以及 reducer

  1. name 用于分割state 命名前缀.
  2. initialState 用于初始化的值.
  3. reducers:{type:fn(state,action)|{reducer,prepare}} 键值对关系映射 , 键用来生成action type ; 值用来生成 reducer ;
  4. extraReducers:fn|map object 执行额外的action 响应的reducer;不会合并到返回值的.actions
    • (builder)=>{builder.addCase().addMatcher().addDefaultCase()}
    • {[action type]:(state,action)=>}

createAsyncThunk(type:string,fn:payloadCallback,{condition,dispatchConditionRejection})

处理一个异步的action type,回掉函数为一个promise; 三种状态,pending\fulfilled\rejected

  1. type action 名称

  2. fn(content:any,thunkAPI) thunkAPI 为调用redux-thunk 所有配置属性,还有其他额外的参数

    • dispatch redux 的dispatch 方法
    • getState 获取redux的所有state数据的方法
    • extra thunk中间件额外的数据
    • requestId 自动生成的唯一的请求序列ID
    • signal 标签当前请求是否需要被取消
    • rejectWithValue 用于默认reject响应返回的数据.
    
    
  3. {condition,dispatchConditionRejection} 可取消当前的异步action 的回调函数;触发rejected 状态action

unwrapResult解析请求响应结果, 捕获请求自身的错误信息,分发reject action

createEntiryAdapter({selectId:(entity)=>,sortComparer:(a,b)=>)})

  • .addOne(state,entity) 添加一个数据
  • .addMany(state,entities:[]|entityObj:{id:entity})
  • .setAll(state,entities:[]|entityObj:{id:entity}) 替换所有已经存在的数据
  • .removeOne(state,id) 移除一个
  • .removeMany(state,ids:[]) 移除多个
  • .updateOne(state,entity) 更新一个
  • .updateMany(state,entitis<entity>:[]) 更新多个
  • .upsertOne(state,entity) 存在则更新(浅比较更新);不存在进行添加
  • .upsertMany(state,entities:[]|entityObj:{id:entity}) 更新多个,不存在则进行添加
  • .getInitialState(state,{option:any}) 获取一个新的初始数据
  • .getSelectors() 返回用于查询数据的select 函数;
    • selectIds 返回ids:[] 数据
    • selectEntities 返回entities:{[id]:entity}
    • selectAll 映射成数组格式列表;和ids顺序保持一致
    • selectTotal 返回所有的数据总数
    • selectById(state,id) 返回数据实体数据中对应id 的数据.

处理某个实体对象的数据状态维护函数 . 比如列表数据的管理、深层次嵌套解析;提供了诸如新增、删除、更新快捷操作.

该模式下的reducer返回的数据格式为{ids:[],entities:{}}

如果你的数据嵌套比较深, 可组合使用normalizr ; 当然如果项目中已经加入了 TS , 就不需要了.

使用工具函数

createSelector

用于复杂数据计算的缓存;数据为发生变化,不会重复计算,直接返回值.(缺点:如果不是触发action的更新reducer操作

,则不能更新)

createDraftSafeSelector

不同于createSelector ,他可以检测到内部的数据更改,并重新计算.

builder.addMatcher()

模式匹配时, 内置的功能函数

  • .isAllof() 匹配给定的所有ation
  • .isAnyOf() 至少匹配一次给定的action
  • .isAsyncThunkAction(...action) 检测给定的action是否符合AsyncThunk
  • .isPending() 给定异步ation是否是请求状态
  • .isFulfilled() 给定的异步action是否是完成状态
  • .isRejected() 给定的异步action 是否是拒绝状态
  • .isRejectedWithValue() 是否使用了rejectedWithValue 的值

current()

获取到当前state变更后的及时数据

感觉有一种要替代合并 redux 的意味 . 内置了好多redux的方法,以及自身的数据状态管理逻辑.

参考资料

redux-toolkit 官方文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

heroboyluck

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值