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. css进阶 02-CSS布局

    02-CSS布局 #前言 #常见的布局属性 (1)display 确定元素的显示类型: block:块级元素. inline:行内元素. inline-block:对外的表现是行内元素(不会独占一行) ...

  2. mini-web框架-元类-总结(5.4.1)

    @ 目录 1.说明 2.代码 关于作者 1.说明 python中万物都是对象 使用python中自带的globals函数返回一个字典 通过这个可以调取当前py文件中的所有东西 当定义一个函数,类,全局 ...

  3. C# HTTP1.0 1.1 2.0与HTTPS 、TCP/IP协议的UDP与TCP、 Socket介绍与WebSocket

    一.HTTP1.0 1.1 2.0和HTTPS 1.HTTP协议是什么? HTTP协议是超文本传输协议的缩写,英文是Hyper Text Transfer Protocol.它是从WEB服务器传输超文 ...

  4. C#中获取DataTable某一列的值转换为集合

    直接使用 //Linqvar l1 = (from d in dt.AsEnumerable() select d.Field<int>("ID")).ToList() ...

  5. [.NET] - 基础知识 - 如何debug一个.NET application

    1.可以使用Debug/Trace类来将runtime信息输出到控制台窗口: https://msdn.microsoft.com/en-us/library/bs4c1wda.aspx https: ...

  6. (十)、cat--查看文件命令

    一.命令描述与格式 将文件或标准输入组合输出到标准输出,所以注定了其可以配合管道应用, 格式:cat    [选项]    [files] 选项: -A   --show-all            ...

  7. 创建Web Service项目

    使用AXIS框架   idea方式: 创建后 加入axis依赖包到输出目录,idea也会提示你进行这步操作 项目启动后访问 http://localhost:8080/AxisWebService/s ...

  8. python对离散数据进行编码

    机器学习中会遇到一些离散型数据,无法带入模型进行训练,所以要对其进行编码,常用的编码方式有两种: 1.特征不具备大小意义的直接独热编码(one-hot encoding) 2.特征有大小意义的采用映射 ...

  9. CF Grakn Forces 2020 1408E Avoid Rainbow Cycles(最小生成树)

    1408E Avoid Rainbow Cycles 概述 非常有趣的题目(指解法,不难,但很难想) 非常崇拜300iq,今天想做一套div1时看见了他出的这套题Grakn Forces 2020,就 ...

  10. 错误总结Mapper扫描不到

    Unsatisfied dependency expressed through field 'baseMapper'; nested exception is org.springframework ...