React + Redux 最佳实践

前端变化虽快,但其实一直都围绕这几个概念在转:

  • URL - 访问什么页面
  • Data - 显示什么信息
  • View - 页面长成什么样
  • Action - 对页面做了什么操作
  • API Server - Data 数据的来源

在 redux 的生态圈内,每个环节有多种方案,比如 Data 可以是 immutable 或者 plain object,在你选了immutable 之后,用 immutable.js 还是 seamless-immutable,以及是否用 redux-immutable 来辅助数据修改,都需要选择。

本文总结目前 react + redux 的最佳实践,解释原因,并提供可选方案。

心急的朋友可以直接看代码:https://github.com/sorrycc/github-stars

一、URL > Data

需求

routing

选择

react-router + react-router-redux: 前者是业界标准,后者可以同步 route 信息到 state,这样你可以在 view 根据 route 信息调整展现,以及通过 action 来修改 route 。

可选

二、Data

需求

为 redux 提供数据源,修改容易。

方案

plain object: 配合 combineReducer 已经可以满足需求。

同时在组织 Store 的时候,层次不要太深,尽量保持在 2 - 3 层。如果层次深,可以考虑用 updeep 来辅助修改数据。

可选

immutable.js: 通过自定义的 api 来操作数据,需要额外的学习成本。不熟悉 immutable.js 的可以先尝试用seamless-immutable,JavaScript 原生接口,无学习门槛。

另外,不推荐用 redux-immutable 以及 redux-immutablejs,一是没啥必要,具体看他们的实现就知道了,都比较简单;更重要的是他们都改写了 combineReducer,会带来潜在的一些兼容问题。

三、Data > View

需求

数据的过滤和筛选。

方案

reselect: store 的 select 方案,用于提取数据的筛选逻辑,让 Component 保持简单。选 reselct 看重的是可组合特性缓存机制

可选

四、View 之 CSS 方案

需求

合理的 CSS 方案,考虑团队协作。

方案

css-modules: 配合 webpack 的 css-loader 进行打包,会为所有的 class name 和 animation name 加 local scope,避免潜在冲突。

直接看代码:

Header.jsx

import style from './Header.less';
export default () => <div className={style.normal} />;

Header.less

.normal { color: red; }

编译后,文件中的 style.normal.normal 在会被重命名为类似 Header__normal___VI1de

可选

bem, rscss ,这两个都是基于约定的方案。但基于约定会带来额外的学习成本和不遍,比如 rscss 要求所有的 Component 都是两个词的连接,比如 Header 就必须换成类似 HeaderBox 这样。

radium,inline css 方案,没研究。

五、Action <> Store,业务逻辑处理

需求

统一处理业务逻辑,尤其是异步的处理。

方案

redux-saga: 用于管理 action,处理异步逻辑。可测试、可 mock、声明式的指令。

可选

redux-loop: 适用于相对简单点的场景,可以组合异步和同步的 action 。但他有个问题是改写了combineReducer,会导致一些意想不到的兼容问题,比如我在特定场景下用不了 redux-devtool 。

redux-thunk, redux-promise 等: 相对原始的异步方案,适用于更简单的场景。在 action 需要组合、取消等操作时,会不好处理。

saga 入门

在 saga 之前,你可能会在 action creator 里处理业务逻辑,虽然能跑通,但是难以测试。比如:

// action creator with thunking

function createRequest () {  
  return (dispatch, getState) => {    
    dispatch({ type: 'REQUEST_STUFF' });    
    someApiCall(function(response) {      
      // some processing
      dispatch({ type: 'RECEIVE_STUFF' });
    });
  };
}

然后组件里可能这样:

function onHandlePress () {  
  this.props.dispatch({ type: 'SHOW_WAITING_MODAL' });  
  this.props.dispatch(createRequest());
}

这样通过 redux state 和 reducer 把所有的事情串联到起来。

但问题是:

Code is everywhere.

通过 saga,你只需要触发一个 action 。

function onHandlePress () {  
  // createRequest 触发 action `BEGIN_REQUEST`
  this.props.dispatch(createRequest());
}

然后所有后续的操作都通过 saga 来管理。

function *hello() {  
  // 等待 action `BEGIN_REQUEST`
  yield take('BEGIN_REQUEST');  
  // dispatch action `SHOW_WAITING_MODAL`
  yield put({ type: 'SHOW_WAITING_MODAL' });  
  // 发布异步请求
  const response = yield call(myApiFunctionThatWrapsFetch);  
  // dispatch action `PRELOAD_IMAGES`, 附上 response 信息
  yield put({ type: 'PRELOAD_IMAGES', response.images });  
  // dispatch action `HIDE_WAITING_MODAL`
  yield put({ type: 'HIDE_WAITING_MODAL' });
}

可以看出,调整之后的代码有几个优点:

  • 所有业务代码都存于 saga 中,不再散落在各处
  • 全同步执行,就算逻辑再复杂,看起来也不会乱

六、Data <> API Server

需求

异步请求。

方案

isomorphic-fetch: 便于在同构应用中使用,另外同时要写 node 和 web 的同学可以用一个库,学一套 api 。

然后通过 async + await 组织代码。

示例代码:

import fetch from 'isomorphic-fetch';
export async function fetchUser(uid) {  
  return await fetch(`/users/${uid}`)
              .then(res => res.json());
};

可选

reqwest

最终

本文来自:https://github.com/sorrycc/blog/issues/1

原文发布于微信公众号 - 星流全栈(MeteorFullStack)

原文发表时间:2016-09-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏杨建荣的学习笔记

关于闪回区溢出导致的数据hang(r11笔记第12天)

对于Oracle数据库的闪回区的设置,之前和一个同事和讨论过,总体来说有一些不同的意见。 首先这个闪回区是一个逻辑的概念,闪回区的大小不会严格依赖于磁盘空间的情...

36213
来自专栏测试开发架构之路

MatlabR2014a 安装破解详细图文教程(附下载链接(内附CVX工具箱))

MATLAB和Mathematica、Maple并称为三大数学软件。它在数学类科技应用软件中在数值计算方面首屈一指。MATLAB可以进行矩阵运算、绘制函数和数据...

3377
来自专栏数据和云

诊断工具与方法:从OS到数据库

最近在“云和恩墨微信大讲堂”中,有很多朋友遇到性能问题,但是往往没有及时的诊断信息。我将之前书中的一章摘录出来和大家略为分享。 在数据库系统的诊断中,通常须要综...

3938
来自专栏恰童鞋骚年

操作系统核心原理-3.进程原理(中):进程调度

PS:在多进程并发的环境里,虽然从概念上看,有多个进程在同时执行,但在单个CPU下,在任何时刻只能有一个进程处于执行状态,而其他进程则处于非执行状态。那么问题来...

1035
来自专栏沃趣科技

容器化RDS|调度策略

导 语 前文数据库容器化|未来已来我们介绍了基于Kubernetes实现的下一代私有 RDS。其中,调度策略是具体实现时至关重要的一环,它关系到RDS 集群的服...

50610
来自专栏walterlv - 吕毅的博客

关闭模态窗口后,父窗口居然跑到了其他窗口的后面

发布于 2018-02-05 05:58 更新于 2018-06...

841
来自专栏LET

谈谈3D Tiles(1):渲染调度

4076
来自专栏Python小屋

Python批量生成垃圾邮件内容

问题背景:这个文章的代码是为下一篇关于贝叶斯分类的文章做准备的,用来生成一些模拟的垃圾邮件。一般而言,垃圾邮件都是带有特定目的的,所以邮件中必然会包含一些特定的...

3786
来自专栏祝威廉

Spark 1.6 内存管理模型( Unified Memory Management)分析

新的内存模型是在这个Jira提出的,JIRA-10000,对应的设计文档在这:unified-memory-management。

1273
来自专栏施炯的IoT开发专栏

Endnote for Windows Mobile

  想必园子里有好多朋友都写过paper吧,在阅读文献的时候,是不是觉得管理文献这个事情很麻烦。我正处于刚刚起步的阶段,英语写译老师Greatlion给我们推荐...

2066

扫码关注云+社区

领取腾讯云代金券