萌新 redux 入门

我不萌,但新。
2018年要结束了,这一年,我。。。算了,很少说了。
想起上半年看的 react,到如今也没怎么用过,都快忘光了,趁如今有时间我来捋捋,首先从 redux 下手(与 react 有啥什么关系???😂)vue

为何用 redux

我想学习 redux 的同窗多少知道它的做用,官方定义 redux 是一个面向 JavaScript 应用的可预测状态容器。若是用过 vuex 的,就能明白它是干吗的,它们做用是同样的,主要用来管理共享状态。不一样于 vuex 与 vue 的关系,redux 与 react 是解耦的,虽然 redux 一般用在 react 中。至于为啥要用 redux,我以为一张图更好说明问题:react

在react应用中,父子组件之间的数据传递比较容易,可是在复杂组件层级关系中,如图左所示的那样,数据的传递就显得很麻烦了,并且容易混乱,这时若是有一个容器帮咱们统一管理数据,并能共享到每一个组件,那就很方便咱们开发了,如图右所示。而redux正是一个提供这样功能的数据框架。vuex

基本概念

概念这东西我很差说,我仍是拿一张图说明:npm

在 react 中使用 redux 时,某组件须要一个数据,那么它就须要触发一个 action,经过 dispatch 给 store,代表本身须要数据啦,action 是行为动做,它告诉 store 须要什么样的数据,它也是 store 数据的惟一来源。store 至关于一个管理员,它本身不直接处理数据,而是把当前的数据和收到的 action 告诉 reducer,让 reducer 来处理数据,并将新数据返还给 store,再由 store 传递给组件,组件拿到的就是目标数据了。redux

初看时,可能有点绕,咱们能够理解为一个在图书馆借书的场景。组件( Component)就至关于借书人,他要告诉管理员(Store),说:"我要借《时间简史》",那么说的这句话就能够理解为 Action,管理员(Store)听到后,他不可能记得每本书的位置,因而就查看记录本看看书的信息,那么这个记录本就至关于 Reducer,找到书的信息后,管理员(Store)就把信息告诉了借书人(Component)。可能比喻并非那么恰当,但表达的意思也差很少了。redux 就是围绕 Action,Store,Reducer 这三点来展开的。浏览器

基本使用

为了方便起见,我直接用 create-react-app 脚手架建立一个项目来演示代码,安装以及建立我就略过了。把项目中多余的文件删除,src 目录下只保留一个 index.js 文件。bash

以前说过,redux 和 react 是解耦的,那么咱们先只在 index.js 文件中来使用一下 redux,使用前别忘记安装如下 reduxapp

npm install redux
复制代码

index.js框架

// 引入 createStore 方法,用于建立 store
import { createStore } from "redux"

// 默认初始状态
const defaultState = {
  a: 1
}

// 建立 reducer
function reducer(state = defaultState, action) {
  switch (action.type) {
    case "ADD":
      return Object.assign({}, state, {
        b: action.num
      });
    default:
      return state;
  }
}

// 建立 store
const store = createStore(reducer)

// 获取 state
console.log('dispatch action 以前的数据:',store.getState())

// 派发 action
store.dispatch({
    type:"ADD",
    num:2
})

// 获取更新后的 state
console.log('dispatch action 以后的数据:',store.getState())

复制代码

运行结果如图:dom

以上就是一个redux的最基本的使用过程,咱们来看下。

reducer

首先说一下 reducer,它用来处理数据,本质上是一个函数,有两个参数 state 和 action,state 便是保存的状态,若是不给他赋初始值,那么它就等于undefined,我在这里给了它一个初始值 defaultState。action 便是行为,它是一个对象,必须有一个名为 type 的字段来表示将要执行的动做,如代码里的ADD就是我传入的一个表示添加的行为,(注意type的值可任意,应尽可能语意化)。除了 type 字段外,你能够任意添加本身须要的字段,好比这里我传里一个 num 字段,我须要将它添加到 state 中去。

reducer的注意事项:

  1. 不要直接修改 state。能够看到,我再代码里使用 Object.assign() 新建了一个副本,再返回的。若不建立副本,redux 的全部操做都将指向内存中的同一个 state,全部的 state 都将被最后一次操做的结果所取代,咱们将没法追溯 state 变动的历史记录。
  2. 在 default 状况下返回旧的 state。即没有 action 时,必定要返回旧的 state。

action

前面说了 action 本质上是一个JavaScript中的对象,约定 action 内必须使用一个字符串类型的 type 字段来表示将要执行的动做。多数状况下,type 会被定义成字符串常量。当应用规模愈来愈大时,建议使用单独的模块或文件来存放 action,以下:

// actionTypes.js
export const ADD = "ADD"
复制代码
import { ADD } from './actionTypes'

const action = {
    type: ADD,
    num: 2
}
复制代码

经过store.dispatch()方法将 action 传到 store。

store

store将 action 和 reducer 联系起来,维持应用的 state,能够经过store.getState()来获取 state,经过 store.dispatch() 来更新 state。咱们经过引入 createStore 方法,并传入 reducer 为第一个参数来建立 store。

在 react 中使用 redux

前面介绍了 redux 的基本使用方法,可是咱们最终仍是要使用在 react 中,如今咱们就结合 react 来用下 redux。

首先,修改项目目录,添加store目录,用来存放 redux 相关文件,而src/index.js文件为组件入口:

咱们就写一个烂大街的计数器的例子,经过加减按钮实现数字的增减。

首先是store中的代码:

actionTypes.js 用来统一存放 action 的 type 类型,并导出

// 增
export const ADD = "ADD";
// 减
export const REDUCE = "REDUCE";
复制代码

reducer.js 建立 reducer 函数,并导出

import { ADD, REDUCE } from "./actionTypes";

const defaultVal = 0;

function reducer(state = defaultVal, action) {
  switch (action.type) {
    case ADD:
      let newVal1 = state + 1;
      return newVal1;
    case REDUCE:
      let newVal2 = state - 1;
      return newVal2;
    default:
      return state;
  }
}

export default reducer;

复制代码

index.js 文件建立 store,并导出

import { createStore } from "redux";
import reducer from "./reducer";

const store = createStore(reducer);

export default store;

复制代码

而后 src/index.js 文件,是组件入口:

import React, { Component } from "react";
import ReactDOM from "react-dom";
import { ADD, REDUCE } from "./store/actionTypes";
import store from "./store";

class APP extends Component {
  render() {
    return (
      <div>
        <div>{store.getState()}</div>
        <div>
          <button onClick={this.handleReduce.bind(this)}>-</button>
          <button onClick={this.handleAdd.bind(this)}>+</button>
        </div>
      </div>
    );
  }

  // 执行减操做
  handleReduce() {
    store.dispatch({
      type: REDUCE
    });
  }

  // 执行加操做
  handleAdd() {
    store.dispatch({
      type: ADD
    });
  }
}

ReactDOM.render(<APP />, document.getElementById("root"));

复制代码

此时运行项目,在浏览器中实现加减操做。可是会发现并无用,怎么点击都没有预想的效果。其实这里少了关键的一步,在 react 中使用不一样于第一个例子里那样,第一个例子中我 dispatch 后会从新获取 state,也就是store.getState(),但在 react 组件里使用时,能够看到我只是获取了一次 state,那么我想要的效果是每当我 dispatch 后,这个 state 会自动更新。

redux 中提供了这样的一个功能,就是store.subscribe(listener),它是 store 下的一个方法,会添加一个变化监听器,每当 dispatch action 的时候就会执行。那么有了监听器,每次 dispatch 时咱们须要执行啥呢?没错,咱们给组件从新 render 下。修改ReactDOM.render这部分代码,以下:

function render() {
  ReactDOM.render(<APP />, document.getElementById("root"));
}
render();

store.subscribe(render);
复制代码

用一个函数包裹ReactDOM.render方法,并执行,这个函数其实就是 listener 了,把它传给 subscribe,这样每次 dispatch 后,就能够更新 state 了。

以上就是结合 react 和 redux 的一个简单入门小例子,我写的不必定清楚,可是代码敲一遍也能了解个大概。

补充

两天后,我以为仍是要补充介绍下 redux 三大原则:

  1. 整个应用只能有一个 store,即const store = createStore(reducer) 这样的建立 store,只能存在一个,保证单一数据源;
  2. state 是只读的,惟一改变 state 的方法就是触发 action。经过dispatch(action)来表达想要修改的意图,全部的修改会被集中化处理;
  3. reducer 必须是纯函数。前面也说过了,reducer 是接收先前的 state 和 action,并返回新的 state,而不是直接修改 state,这就是符合纯函数的约束。

什么是纯函数?

  • 不能改写参数,咱们是经过参数返回新的数值的,一样的输入,获得一样的输出
  • 不能调用系统 I/O 的API
  • 不能调用Date.now()或者Math.random()等不纯的方法,由于每次会获得不同的结果

文中有写的不对的地方,望有大佬指点。

相关文章
相关标签/搜索