Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >如何更优雅地使用 Redux

如何更优雅地使用 Redux

原创
作者头像
陈津
修改于 2017-09-06 01:36:50
修改于 2017-09-06 01:36:50
2.7K2
举报
文章被收录于专栏:陈津的专栏陈津的专栏

业务背景介绍:腾讯云数据库产品中心 & 大数据人工智能产品中心 前端从2016年初开始尝试 React + Redux 全家桶,期间经历了很多波折,到目前为止总共28个项目,其中有15个项目使用了该方案

一、Redux开发噩梦

Redux 在我看来除了提供统一的状态管理,最大好处就是实现 视图、业务逻辑 与 数据处理的分离,这样可以最大程度地去复用三个模块。

图:Redux 对项目的模块拆分

从这种意义上来说,它是成功的,但是实际的开发过程中,却遇到很多问题,导致开发体验非常不友好。

1、丑陋的switch case

做过 Redux 开发的一定对 Reducer 不陌生,里面主要靠 switch case 来处理 action。对于一个状态复杂的应用,一般使用 combineReducers来进行模块拆分,进而减少switch case的长度,使得模块化的 Reducer 可维护。实际应用中,往往比较考验开发者的模块划分能力,一些比较复杂的模块,不进行很好的拆分和重构,伴随着业务的变化 switch case 任然会增长很长。但如果你拆分得过细,Reducer与应用的状态树就会变得复杂。

下面是一个典型的表格 reducer:

代码语言:txt
AI代码解释
复制
var initailState = {
    columns,
    isLoading: true,
    list: [],
    result: undefined,
    selectedSet: new Set(),
    searchKey: ''
};

export default function (state = initailState, action) {
    switch (action.type) {
        case actions.ON_SEARCH:
            return {
                ...state,
                searchKey: action.searchKey
            };
        case actions.ON_TABLE_RELOAD://重新加载
            return {
                ...state,
                isLoading: true,
                selectedSet: new Set()
            };
        case actions.ON_TABLE_LOADED://加载完成
            return {
                ...state,
                isLoading: false,
                list: action.result && action.result.data && action.result.data.mixIpList || [],
                result: action.result
            };
        case actions.ON_TABLE_ALL_CHECK_CHANGE://全选
        {
            const {isCheck} = action,
                {list} = state;
            let selectedSet = new Set();
            if (isCheck) {
                list.map((item)=>selectedSet.add(item.id));
            }
            return {...state, selectedSet};
        }
        case actions.ON_TABLE_ITEM_SELECT_CHANGE://选中一项
        {
            const {id, isCheck} = action;
            let {selectedSet, list} = state;
            if (isCheck) {
                selectedSet.add(id);
            } else {
                selectedSet.delete(id);
            }
            return {...state, selectedSet};
        }
        case actions.ON_PROJECT_LOADED:
        {
            if(state.list.length){
                var plist = action.list;
                var li = state.list.concat();
                li.forEach(function (vip) {
                    plist.forEach(function (proj) {
                        if(vip.projectId == proj.value){
                            vip.projectName = proj.text;
                        }
                    });
                });
                return {...state, list: li};
            }else{
                return state;
            }
        }
        default:
            return state;
    }
}

2、令人头晕的开发体验

Redux 要实现 视图、业务逻辑 与 数据处理的分离,其实默认要求开发者开发过程是纵向的,但实际的开发过程中,大多数人的开发过程是横向的,如下图:

图:开发过程

这就导致一个问题,开发者会在 Reducer、ActionCreator、View 三者来回切换开发,在阅读一个项目源码的时候,也需要来回切换查阅,才能清晰地知道某个模块的逻辑。当模块的 Action 足够多,足够复杂,并且你显示器又不够大的时候,上面的过程往往就会把你绕晕了。

二、如何更优雅地使用

经历了很多项目,我观察到 Reducer 的一个代码特点,大量的 switch case 下都是简单的数据加工合成新的状态子树,这里可以通过统一的扩展覆盖方式来实现这个目标。

首先,我将 Dispatch 的方法设计为:

代码语言:txt
AI代码解释
复制
dispatch({
    type: actions.ON_REPORT_LOAD_COMPLETED,
    report:{
        isLoadingError: false,
        original: result.data[0],
        compare: result.data.length > 1 ? result.data[1] : null,
    }
})

这样,依靠关键字 report 可以用来做 Reducer 匹配,对应 report里面的内容可以直接在原有状态子树的基础上扩展覆盖生成新的状态子树。

对应 report的 Reducer 设计如下:

代码语言:txt
AI代码解释
复制
function reportReducer(
    state = {
        isLoadingError: false,
        original:{},
        compare:{}
    },
    action) {
    let newState = {...state, ...action['report']};
    return newState;
}

按照上述的方法,我们就解决了switch case的问题,action.type在这里的作用就只有 Redux DevTools 的回溯才会用到。

还可以近一步地优化,可以写一个方法来返回 Reducer 方法,这样就不用再重复写相同 Reducer 的扩展逻辑,如下:

代码语言:txt
AI代码解释
复制
function autoReducerCreator(initializeState, id) {
    return (state = initializeState, action) => {
        let updateState = action[id];

        //没有更新的状态,不进行处理
        if(typeof updateState == 'undefined'){
            return state;
        }

        //只更新原始状态子树有的属性
        //let newState = {...state, ...action[id]};
        let newState = {};
        for(let key in state){
            if(updateState.hasOwnProperty(key)){
                newState = updateState[key];
            } else{
                newState = state[key];
            }
        }


        return newState;
    }
}

//生成 reportReducer
let reportReducer = autoReducerCreator({
    isLoadingError: false,
    original:{},
    compare:{}
}, 'report');

let mainReducer = autoReducerCreator({
    isLoadingError: false,
    title: '-',
    content: '-'
}, 'main');

//组合 reducer
let reducers = combineReducers({
    report: reportReducer,
    main: mainReducer
});

最后还可以近一步创建一个函数分析状态对象,自动生成 Reducer,使得接口调用更简单,对整个 Redux 状态树更直观,如下:

代码语言:txt
AI代码解释
复制
function combineAutoReducers(initializeState){
    let reducers = {};
    for(let key in initializeState){
        let subState = initializeState[key];
        reducers[key] = autoReducerCreator(subState, key);
    }
    return combineReducers(reducers);
}

combineAutoReducers({
    report:{
        isLoadingError: false,
        original:{},
        compare:{}
    },
    main:{
        isLoadingError: false,
        title: '-',
        content: '-'
    }
})

三、最后

回到第一张图 Redux 的本意应该是数据与业务分离,数据处理的代码被分割到 Reducer 里,而业务逻辑放到 ActionCreator 里,而上述的优雅方案从某种程度上来会打破这种设定。但我想说的是这是一种折中,将 Reducer 90%代码压缩掉,剩余10%的数据处理代码不可避免的分散到 ActionCreator里,经过实际项目经历,其他同事均反馈开发效率与代码阅读体验得到很大提升。

当然最后的这个工具也保留了对原生 Reducer 的兼容方法。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
2 条评论
热度
最新
函数分析状态对象,自动生成 Reducer,使得接口调用更简单。我觉得这一步很重要
函数分析状态对象,自动生成 Reducer,使得接口调用更简单。我觉得这一步很重要
回复回复点赞举报
谢谢分享!
谢谢分享!
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
Redux系列x:源码分析
写在前面 redux的源码很简洁,除了applyMiddleware比较绕难以理解外,大部分还是 这里假设读者对redux有一定了解,就不科普redux的概念和API啥的啦,这部分建议直接看官方文档。
IMWeb前端团队
2017/12/29
1.3K0
redux源码解析
applyMiddleware.js import compose from './compose' /** * Creates a store enhancer that applies middleware to the dispatch method * of the Redux store. This is handy for a variety of tasks, such as expressing * asynchronous actions in a concise manner,
theanarkh
2019/03/06
1.2K0
状态管理的概念,都是纸老虎
不管是Vue,还是 React,都需要管理状态(state),比如组件之间都有共享状态的需要。什么是共享状态?比如一个组件需要使用另一个组件的状态,或者一个组件需要改变另一个组件的状态,都是共享状态。
Nealyang
2021/04/20
5.4K0
优雅地乱玩 Redux-1-Getting Started
Redux 基于严格单向数据流实现 反正就是为了打破 React 自身的数据流而创建的
szhshp
2022/09/21
2390
Redux 入门教程(一):基本用法
一年半前,我写了《React 入门实例教程》,介绍了 React 的基本用法。 React 只是 DOM 的一个抽象层,并不是 Web 应用的完整解决方案。有两个方面,它没涉及。 代码结构 组件之间的通信 对于大型的复杂应用来说,这两方面恰恰是最关键的。因此,只用 React 没法写大型应用。 为了解决这个问题,2014年 Facebook 提出了 Flux 架构的概念,引发了很多的实现。2015年,Redux 出现,将 Flux 与函数式编程结合一起,很短时间内就成为了最热门的前端架构。 本文详细介绍
ruanyf
2018/04/12
1.1K0
Redux 入门教程(一):基本用法
React+Redux的一个简单开发实例
看到这个水果篮子的样子,大家应该可以明白它的功能:你可以做两件事 — 摘苹果和吃苹果。当你摘苹果时,应用会向后台发送ajax请求索取苹果,每个苹果有两个属性:编号和重量。当你吃苹果掉时,不用告诉后台,在前端偷偷吃掉就好啦~ 同时苹果篮子会显示当前的苹果量和已经吃掉的苹果量。好!那下面我们来开工!
javascript.shop
2019/09/04
1.4K0
React+Redux的一个简单开发实例
React结合Redux实现Todolist
使用了redux进行状态管理 删除一个用户首先dispatch了一个deleteTodoList的 action  然后在reducer中返回删除后的数据 store更新数据 组件订阅了store的更新后 更新了内部状态 页面更新成功
憧憬博客
2020/07/20
5250
【React】360- 完全理解 redux(从零实现一个 redux)
记得开始接触 react 技术栈的时候,最难理解的地方就是 redux。全是新名词:reducer、store、dispatch、middleware 等等,我就理解 state 一个名词。
pingan8787
2019/09/25
7820
【React】360- 完全理解 redux(从零实现一个 redux)
使用Redux制作一个TodoList
在组件化开发的 web 前端当中,经常的需要在不同的组件之间进行通信以及一些数据共享,那么我们就需要使用像 Vuex 那样的状态管理工具,在 React 当中,经常使用 Redux 来做状态管理工具。
小小杰啊
2022/12/21
4870
使用Redux制作一个TodoList
【THE LAST TIME】从 Redux 源码中学习它的范式
「范式」概念是库恩范式理论的核心,而范式从本质上讲是一种理论体系。库恩指出:「按既定的用法,范式就是一种公认的模型或模式」。
Nealyang
2020/05/05
4230
造一个 redux 轮子
Redux 应该是很多前端新手的噩梦。还记得我刚接触 Redux 的时候也是刚从 Vue 转过来的时候,觉得Redux 概念非常多,想写一个 Hello World 都难。
写代码的海怪
2022/03/29
1.6K0
造一个 redux 轮子
深入学习 Redux 之基础用法
① Web 应用是一个状态机,视图与状态是一一对应的。 ② 所有的状态,保存在一个对象里面。
Leophen
2021/07/16
4880
深入学习 Redux 之基础用法
Redux开发实用教程
为了帮助大家快速上手什么是Redux开发,在这本节中将向大家介绍什么是Redux开发所需要的一些什么是Redux必备基础以及高级知识。
CrazyCodeBoy
2019/12/10
1.5K0
Redux开发实用教程
Redux使用总结
创建redux必备3文件(redux架构), store.js actions.js reducers.js 创建 [ store.js ] [ reducers.js ] [ actions.js ] (内容可以先空,具体业务在来补充)
用户1065635
2019/03/21
8170
深入学习和理解 Redux
Redux官网上是这样描述Redux,Redux is a predictable state container for JavaScript apps.(Redux是JavaScript状态容器,提供可预测性的状态管理)。 目前Redux GitHub有5w多star,足以说明 Redux 受欢迎的程度。
2020labs小助手
2020/03/05
9080
手把手教你全家桶之React(二)
前言 上一篇已经讲了一些react的基本配置,本遍接着讲热更新以及react+redux的配置与使用。 热更新 我们在实际开发时,都有用到热更新,在修改代码后,不用每次都重启服务,而是自动更新。并而不
用户2145235
2018/05/18
1.7K0
Redux源码分析
顾名思义就是将多个reducer合成一个reducer。传入的reducer是一个对象,对象的键值是reducer的名称。
IMWeb前端团队
2019/12/03
3540
Redux源码分析
从 Redux 设计理念到源码分析
Redux 也是我列在 THE LAST TIME 系列中的一篇,由于现在正在着手探究关于我目前正在开发的业务中状态管理的方案。所以,这里打算先从 Redux 中学习学习,从他的状态中取取经。毕竟,成功总是需要站在巨人的肩膀上不是。
Nealyang
2020/04/26
9750
从 Redux 设计理念到源码分析
Redux介绍及源码解析
这篇文章来总结一下 Redux, 便于以后的知识回顾. 有了之前 Flux 知识学习, 应该对单向数据流的状态管理有比较清晰的认识了, 同样 Redux 的出现也是受到了 Flux 的启发, 这也是我们最好要先去了解一下 Flux 的原因. 同时 Redux 利用纯函数简单明了的特点, 在 Flux 架构的基础上进行了优化和功能增强 (支持中间件、异步等), 降低了复杂度, 同时还提供强大的工具库支持 (React-Redux、Redux-Toolkit、Redux-Thunk). 下面一起来看下其具体的实现逻辑. 详细内容可以直接在官网学习.
BLUSE
2022/11/27
2.6K1
你想要的——redux源码分析
备注:例子中结合的是react进行使用,当然redux不仅仅能结合react,还能结合市面上其他大多数的框架,这也是它比较流弊的地方
can4hou6joeng4
2023/11/30
2390
相关推荐
Redux系列x:源码分析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档