最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Redux系列 - redux/toolkit

    正文概述 掘金(hboot)   2021-02-12   1085

    解决什么问题:

    • 太多的样板文件、模版代码(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,不能直接修改;现在可以了:face_with_thermometer:

    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
        }
    })
    

    示例中为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 模块的reducer函数
    • .actions 自动生成的actions creator 方法
    • .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

    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:{}}

    使用工具函数

    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-toolkit 官方文档


    起源地下载网 » Redux系列 - redux/toolkit

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元