React 将页面元素拆分成组件,通过组装展示数据。组件又有无状态有状态之分,所谓状态,可以简单的认为是组件要展示的数据。React 有个特性或者说是限制单向数据流,组件的状态数据只能在组件内部修改,对于其他组件是只读的,想要修改只能通过组件提供的接口回调。

随着组件数量的增多,组件间状态数据共享的复杂性也会随之增加,如果仅使用 React 组件内的 State 可能会导致程序的流程混乱,代码难以维护。

原文链接:https://www.chuonye.com/archives/react-redux.html

1. 引入 Redux

为什么这么说呢,我们来看一个图:

原始的 React,当最底层组件需要改变数据时,如果数据在父组件,回调方法层层传递即可;如果在兄弟组件,那就需要借助一个中间组件,当然了也有办法直接与兄弟组件通信。可以想象,随着应用复杂程度的提高,组件间通信使用的各种直接或间接回调,可能会导致代码乱成一团。

此时,Redux 就派上用场了,如上图所示,它把应用的所有状态-数据存储在一个地方,并称之为Store,组件间不直接通信,而是把变化的数据推给 Store,需要根据状态变化重新渲染的组件通过订阅 Store 来实现。

Redux 在设计与实现时,遵循三大原则或者说规范限制

1.1 唯一数据源

整个应用程序的状态数据仅存储在一个 Store 中,数据就存储在一个大的对象树(object tree)中。

1.2 只读的 Store

唯一的 Store 也是只读的,应用程序无法直接修改状态数据。Store 对象本身的 API 非常少,仅有四个方法:

  • getState() : 获取当前状态数据
  • dispatch(action) : 推送触发变化
  • subscribe(listener) : 订阅数据变化
  • replaceReducer(nextReducer)

显而易见,没有提供设置状态的方法。其中的 dispatch(action) 是唯一改变数据的方法,比如:

var action = {
type: 'ADD_USER',
user: {name: 'chuonye.com'}
}; store.dispatch(action);

dispatch 方法会将 action 传递给 Redux,action 就是一个普通对象,包含触发的操作类型以及数据。

1.3 使用纯函数更改数据

Redux 接收到 action 后,会使用一个纯函数来处理,这些函数被称为 Reducers

var someReducer = function(state, action) {
...
return state;
}

Reducer 接收当前的 state 和 action 作为参数,它也不能直接修改原有的数据,而是通过返回一个新的 state 来修改。

总的来说,Redux 是一个帮助应用统一管理状态数据的工具,它遵循严格的单向数据流(Store 只读)设计,使得应用的行为变得可预测且容易理解。

2. React-Redux

Redux 一般和 React 这类框架搭配使用,为了方便与 React 集成,Redux 官方提供了一个 react-redux 绑定库。react-redux 将组件划分为容器组件UI组件其他组件,其中:

  • 容器组件:与 Redux-Store 交互,分派 Action,监听 state 变化,负责数据管理和业务逻辑
  • UI 组件:无状态,负责数据的展示,样式,排版,数据来源于 props 属性
  • 其他组件:无法明确区分是容器还是 UI 的组件,或者本身比较简单没有拆分必要的组件

从整体来看,在使用上,应用代码分层设计,结构如下:

我们不妨结合着数据库,来理解下每个层次的意思,从下往上:

  • Store 可以理解成数据库服务,其中的 State 是一个数据库,State 对象树的各个部分,可以理解为对应不同的
  • Reducer 就是实际对 State 增删改的,它按逻辑拆分成多个 子reducer,分别对对象树的不同部分或者说对不同的表进行操作,就如同一个 SQL 执行引擎
  • Action 就是一系列触发改变的动作以及数据,就如同一条条 SQL 语句
  • 容器组件和 UI 组件就如同 Java 中设计的 DAO 和 Controller

这些设计怎么有点熟悉,跟 Java 是越来越像了。下面简单写个例子,看看到底怎么用。

3. 实例:Counter 计数器

接下来我们按照使用原生 React,使用 Redux 和使用 React-Redux的顺序分别实现一个 Counter 计数器的功能。

3.1 原生 React 实现

import React, { Component } from 'react';
import ReactDOM from 'react-dom'; class Counter extends Component {
constructor(props) {
super(props);
this.state = { value: 0 }; // 状态数据
}
render() {
return (
<div>
<p>state: {this.state.value}</p>
<button onClick={() => this.handleIncrement()}>+1</button>
<button onClick={() => this.handleDecrement()}>-1</button>
</div>
);
}
// 处理方法
handleIncrement() {
let curVal = this.state.value + 1;
this.setState({value: curVal});
}
handleDecrement() {
let curVal = this.state.value - 1;
this.setState({value: curVal});
}
}
// 渲染
ReactDOM.render(<Counter />, document.getElementById('root'))

3.2 使用 Redux 实现

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux'; // 接收 action 并处理,签名:(state, action) => newState
function reducer(state = {value: 0}, action) {
let curVal = state.value;
switch (action.type) {
case 'INCREMENT':
return {value: curVal+1};
case 'DECREMENT':
return {value: curVal-1};
default:
return state;
}
}
// 创建 Redux store
const store = createStore(reducer); const render = () => ReactDOM.render(
<div>
<p>state: {store.getState().value}</p>
{/* dispatch 通知 store 改变数据 */}
<button onClick={()=>{store.dispatch({type: 'INCREMENT'})}}>+1</button>
<button onClick={()=>{store.dispatch({type: 'DECREMENT'})}}>-1</button>
</div>,
document.getElementById('root')
) render();
// 订阅 store,状态改变时更新 UI
store.subscribe(render);

3.3 使用 React-Redux 实现

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux'; // 接收 action 并处理
function reducer(state = {value: 0}, action) {
let curVal = state.value;
switch (action.type) {
case 'INCREMENT':
return {value: curVal+1};
case 'DECREMENT':
return {value: curVal-1};
default:
return state;
}
}
// 定义一个 UI 组件,用于展示数据
class CounterUI extends Component {
render() {
// 数据来源于 props
const {value, handleIncrement, handleDecrement} = this.props;
return (
<div>
<p>state: {value}</p>
<button onClick={handleIncrement}>+1</button>
<button onClick={handleDecrement}>-1</button>
</div>
);
}
}
// 定义一个 容器组件,用于与 Redux store 交互
// 将 Redux state 按需注入到 UI 组件的 props
function mapStateToProps(state) {
return { value: state.value }
} // 将 Redux actions 按需注入到 UI 组件的 props
function mapDispatchToProps(dispatch) {
return {
handleIncrement: () => dispatch({type: 'INCREMENT'}),
handleDecrement: () => dispatch({type: 'DECREMENT'})
}
}
// 使用 connect 方法基于 UI组件 生成一个 容器组件
const CounterContainer = connect(mapStateToProps, mapDispatchToProps)(CounterUI); const store = createStore(reducer); // 使用 Provider 作为根组件,使得所有子组件都能访问到 store
ReactDOM.render(
<Provider store={store}>
<CounterContainer />
</Provider>,
document.getElementById('root')
)

4. 小结

Redux 和 React-Redux 专门设计了约束和约定,导致代码量不见得减少,甚至还可能增加了。简单的项目看不出有什么优势,但在构建一定规模的企业级项目时,这些约束和条条框框,会有利于项目的维护和管理。正如 Java,有些人认为过于啰嗦,但这正是它严谨的体现!

下一篇将会结合 antd UI框架模仿实现官方提供的购物车实例。

React 入门-redux 和 react-redux的更多相关文章

  1. arcgis api 4.x for js 结合 react 入门开发系列react全家桶实现加载天地图(附源码下载)

    基于两篇react+arcgis的文章介绍,相信大家也能体会两者的开发区别了.在“初探篇”中作者也讲述了自己的选择,故废话不多说,本篇带大家体验在@arcgis/webpack-plugin环境下,使 ...

  2. React入门看这篇就够了

    摘要: 很多值得了解的细节. 原文:React入门看这篇就够了 作者:Random Fundebug经授权转载,版权归原作者所有. React 背景介绍 React 入门实例教程 React 起源于 ...

  3. [转]React入门看这篇就够了

    摘要: 很多值得了解的细节. 原文:React入门看这篇就够了 作者:Random Fundebug经授权转载,版权归原作者所有. React 背景介绍 React 入门实例教程 React 起源于 ...

  4. React入门资源整理

    另外,附上我搜集的一些比较实用的学习资料,建议先看这些撸起来,再看什么乱七八糟的awsome系列. React入门资源整理 React项目新手指南 http://www.w3ctech.com/top ...

  5. 推荐一个React 入门的教程

    推荐一个React 入门的教程 react 入门实例教程 Github地址:https://github.com/ruanyf/react-demos

  6. Flux --> Redux --> Redux React 入门

    本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...

  7. Flux --> Redux --> Redux React 入门 基础实例使用

    本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...

  8. react系列(四)Redux基本概念和使用

    Redux基本概念和使用 先从Flux开始 先放一个Flux官网的链接.需要fq. Flux是Facebook提出的一种构建客户端网页应用的应用架构,它是一种抽象程度很高的设计模式,鼓励单向数据流. ...

  9. React:快速上手(5)——掌握Redux(2)

    React:快速上手(5)——掌握Redux(2) 本文部分内容参考阮一峰的Redux教程. React-Redux原理 React-Redux运行机制 我觉得这张图清楚地描述React-Redux的 ...

  10. React:快速上手(4)——掌握Redux(1)

    React:快速上手(4)——掌握Redux 引入Redux 混乱的state管理 随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状 ...

随机推荐

  1. vue第十一单元(内置组件)

    第十一单元(内置组件) #课程目标 熟练掌握component组件的用法 熟练使用keep-alive组件 #知识点 #1.component组件 component是vue的一个内置组件,作用是:配 ...

  2. 移动端 canvas基础1

    一.canvas画布 Canvas是HTML5中新出的一个元素,开发者可以通过JS脚本动态绘制图像. #1. 创建canvas画布 在页面中创建canvas标签,并设置其id和宽高 (不要通过css设 ...

  3. vue 实现一个商城项目

    在学习了 vue 之后,决定做一个小练习,仿写了一个有关购物商城的小项目.下面就对项目做一个简单的介绍. 项目源码: github 项目的目录结构 -assets 与项目有关的静态资源,包括 css, ...

  4. C#中的深度学习(二):预处理识别硬币的数据集

    在文章中,我们将对输入到机器学习模型中的数据集进行预处理. 这里我们将对一个硬币数据集进行预处理,以便以后在监督学习模型中进行训练.在机器学习中预处理数据集通常涉及以下任务: 清理数据--通过对周围数 ...

  5. 使用h5开发跨平台APP确保数据安全交互---服务器篇

    从eclipse到android studio的安卓开发经验告诉我原声开发才是硬道理,其实以前很抵触html5开发app的,虽然没有去了解过,但是冥冥中就觉得它运行速度太慢了,加载渲染根本比不上原生开 ...

  6. (八)、rm--删除文件或者目录

    一.命令的描述与格式 永久地删除文件或者目录,此命令具有破坏性,一旦删除,没有备份,无法恢复 格式:rm  [选项]  文件或者目录 -d或者--directory                  ...

  7. Dubbo服务调用过程源码解析④

    目录 0.服务的调用 1.发送请求 2.请求编码 3.请求的解码 4.调用具体服务 5.返回调用结果 6.接收调用结果 Dubbo SPI源码解析① Dubbo服务暴露源码解析② Dubbo服务引用源 ...

  8. java中对list集合中的数据按照某一个属性进行分组

    import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Ite ...

  9. git使用上

    因为最近工作上多处都用到了基于 Git 的开发,需要深入理解 Git 的工作原理,以往的 Git 基本知识已经满足不了需求了,因此写下这篇 Git 进阶的文章,主要是介绍了一些大家平时会碰到但是很少去 ...

  10. svg基础--基本语法与标签

    svg系列–基础 这里会总结svg的基础知识和一些经典的案例. svg简介 SVG(Scalable Vector Graphics)is an XML-based Language for crea ...