彻底捋顺redux

仔细算来,redux用了也有一年多了,但一直是用的时候捡起来,不用又忘了的情况,处在似懂非懂的阶段。这篇文章用来对redux做一个总结,彻底搞懂redux。


Redux是JavaScript状态容器,提供可预测化的状态管理。redux提出的目的是为了解决大型的复杂应用中组件之间的通信困难、代码结构混乱等问题。具体来说就是以下几种场景:
就使用场景来说,

  • 用户的使用方式复杂
  • 不同身份的用户有不同的使用方式(比如普通用户和管理员)
  • 多个用户之间可以协作
  • 与服务器大量交互,或者使用了WebSocket
  • View要从多个来源获取数据

就组件场景来说,

  • 某个组件的状态,需要共享
  • 某个状态需要在任何地方都可以拿到
  • 一个组件需要改变全局状态
  • 一个组件需要改变另一个组件的状态

发生上面情况时,如果不使用 Redux 或者其他状态管理工具,不按照一定规律处理状态的读写,代码很快就会变成一团乱麻。你需要一种机制,可以在同一个地方查询状态、改变状态、传播状态的变化。

开始之前先来了解几个概念

Redux 由以下组件组成:
  • Action,这是一个用来描述发生了什么事情的对象。
  • Reducer,接收action并更新Store。
  • Store,整个程序的状态/对象树保存在Store中。
  • View,只显示 Store 提供的数据。
Redux有三大原则
  • 单一数据源store:store是整个应用的数据存储中心(store tree),集中大部分页面需要的状态数据;
  • state是只读的,所以需要view触发action去更新store;
  • 使用纯函数Reducer执行state更新。
image.png

下面开始逐一介绍

  1. Store
    Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。Redux 提供createStore这个函数,用来生成 Store。store提供getState方法获取所有的state
import { createStore } from 'redux';
const store = createStore(reducer);

const state = store.getState();
  1. Action
    上面说过state是只读的,State 的变化会导致 View 的变化。但是用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。
const action = {
  type: 'ADD',
  payload: 1
};

Action 是一个对象。其中的type属性是必须的,表示 Action 的名称。Action 描述当前发生的事情。改变 State 的唯一办法,就是使用 Action。它会运送数据到 Store。
定义好Action了,怎么发出呢?store.dispatch()是 View 发出 Action 的唯一方法。

import { createStore } from 'redux';
const store = createStore(reducer);

store.dispatch(action);
  1. reducer
    Reducer 是一个纯函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
// 这里reducer也可以进行拆分,放到单独一个文件中
import { createStore } from 'redux';

const defaultState = 0;
const increaseReducer = (state = defaultState, action) => {
  switch (action.type) {
    case 'ADD':
      return state + action.payload;
    default: 
      return state;
  }
}
const store = createStore(increaseReducer);

上面代码中,由于store.dispatch方法会触发 Reducer 的自动执行,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore方法。
createStore接受 Reducer 作为参数,生成一个新的 Store。以后每当store.dispatch发送过来一个新的 Action,就会自动调用 Reducer,得到新的 State。

Reducer 函数最重要的特征就是它必须是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。纯函数是函数式编程的概念,必须遵守一些约束:

  • 不得改写参数
  • 不能调用系统 I/O 的API
  • 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果

ps: reducer拆分
试想一下,在一个大型应用中 State 必然十分庞大,导致 Reducer 函数也十分庞大。在上面的例子中我们提到reducer也可以进行拆分,放在一个文件里面,然后统一引入。Redux 提供了一个combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。

import { combineReducers } from 'redux'
import increase from './increase'
import decrease from './decrease'

const reducer = combineReducers({
  value: increase,
  decrease  // 这种写法有一个前提,就是 State 的属性名必须与子 Reducer 同名。否则就用上面这种写法
})
export default reducer;

  1. React-Redux 的用法
    React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。

UI 组件有以下几个特征:

  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 没有状态(即不使用this.state这个变量)
  • 所有数据都由参数(this.props)提供
  • 不使用任何 Redux 的 API

容器组件的特征如下:

  • 负责管理数据和业务逻辑,不负责 UI 的呈现
  • 带有内部状态
  • 使用 Redux 的 API
connect

React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。

import { connect } from 'react-redux'
import mapStateToProps from ''
import mapDispatchToProps  from ''

const App= connect(
  mapStateToProps,   // value 来自这里
  mapDispatchToProps   // onIncreaseClick 来自这里
)(Counter)

class Counter extends Component {
  render() {
    const { value, onIncreaseClick } = this.props
    return (
      <div>
        <span>{value}</span>
        <button onClick={onIncreaseClick}>Increase</button>
      </div>
    )
  }
}

export default App

上面代码中,Counter是 UI 组件,App就是由 React-Redux 通过connect方法自动生成的容器组件。

mapStateToProps

mapStateToProps是一个函数。它的作用就是像它的名字那样,建立一个从state对象(外部的)到props对象(UI 组件的)的映射关系。mapStateToProps执行后返回一个对象,里面的每一个键值对就是一个映射。

const mapStateToProps = state => {
  return {
    value: state.value
  }
}

mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。

mapDispatchToProps

mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。

const mapDispatchToProps = dispatch=> {
  return {
    onIncreaseClick: () => {
      dispatch({
        type: 'ADD',
        payload: 1
      });
    }
  };
}
<Provider> 组件

connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。如果一级级将state传下去就很麻烦,React-Redux 提供Provider组件,可以让容器组件拿到state。

import { Provider } from 'react-redux'
import { createStore } from 'redux'
import reducer from './reducers'
import App from './app'

let store = createStore(reducer);

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

上面代码中,Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了

redux可以使代码结构更加规范,代码可读性更强。因为React提出将展示组件/UI组件(业务逻辑)与容器组件(数据源)分离的思想,所以降低了React 与Redux之间的耦合度。


为什么我能够看得更远,那是因为我站在巨人的肩上,以上内容来源:
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容