1- Redux工作流程(摘自React小书和知乎redux入门介绍)

redux 入门介绍

从这节起我们开始学习 Redux,一种新型的前端“架构模式”。经常和 React.js 一并提出,你要用 React.js 基本都要伴随着 Redux 和 React.js 结合的库 React-redux。

专注于状态管理的库

  • redux专注于状态管理,和react解耦
  • 单一状态,单向数据流
  • 核心概念 : store,state,action,reducer(拿到老的state和action,生成新的state)

为什么使用redux
不同的模块(组件)之间确实需要共享数据,这些模块(组件)还可能需要修改这些共享数据。这里的矛盾就是:“模块(组件)之间需要共享数据”,和“数据可能被任意修改导致不可预料的结果”之间的矛盾。我们必须通过一个“中间人” —— dispatch,所有的数据修改必须通过它,并且你必须用 action 来大声告诉它要修改什么,只有它允许的才能修改.我们再也不用担心共享数据状态的修改的问题,我们只要把控了 dispatch,所有的对 appState 的修改就无所遁形,毕竟只有一根箭头指向 appState 了。

1.安装

安装 redux 核心和 react-redux 集成进 react

$ npm install --save redux
$ npm install --save react-redux

2.API

redux store

在 redux 中 store 作为一个单例,存放了应用的所有数据,对外提供了如下的 API:

  1. 获取数据 store.getState()

  2. 通过触发 action 来更新数据 store.dispatch(action)

  3. pubsub 模式提供消息订阅和取消 store.subscribe(listener)

创建并初始化 store

redux 提供 createStore 方法来创建一个 store

function createStore(reducer, initialState) {
    // ....
}

触发 action

redux 修改应用数据的唯一方式为 store.dispatch(action)

所有对数据的操作必须通过 dispatch 函数。它接受一个参数 action,这个 action 是一个普通的 JavaScript 对象,里面必须包含一个 type 字段来声明你到底想干什么。dispatch 在 swtich 里面会识别这个 type 字段,能够识别出来的操作才会执行对 appState 的修改。

store.dispatch({
  type: 'ADD_TODO',
  title: 'new todo'
})

消息订阅和取消

为了让用户监听应用数据改变,redux 在 store 集成了 pubsub 模式

订阅

// 每次应用数据改变,输出最新的应用数据
store.subscribe(function(){
  console.log(store.getState())
})

// 如果新增了 todo 会触发上面的订阅函数
store.dispatch({
  type: 'ADD_TODO',
  title: 'new todo'
})

取消

subscribe 返回的函数即为取消函数

var unsubscribe = store.subscribe(function(){
  console.log(store.getState())
})

// ....

unsubscribe();

设计应用数据结构

所有数据都存放在一个 store 中,以 todoApp 为例子,state 的数据结构可能为

{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux',
      completed: true,
    },
    {
      text: 'Keep all state in a single tree',
      completed: false
    }
  ]
}
import React,{Component} from 'react'
import ReactDOM from 'react-dom'
import {
    createStore
} from 'redux'

const increase = document.querySelector('.increase')
const decrease = document.querySelector('.decrease')

//创建一个store
let store = createStore(counter,0)

// 改变状态,发布一个action,每次dispath,都会重新计算reducer
document.onclick = ()=>{
    store.dispatch({
        type : "INCREASE"
    })
}

// 监听状态改变
store.subscribe(()=>{
    let state = store.getState()
    console.log(state)
})

//得到reducer返回的整个应用的状态
store.getState()
console.log(store.getState())

//reducer,返回整个应用的状态
function counter(state,action){
    // 取出action中的type
    let {type} = action

    switch (type) {
        case "INCREASE" : 
            return state+1
            break
        default : 
            return state
    }

    return state
}

ReactDOM.render(
    <div>11</div>,
    document.getElementById('root')
)

创建action : actionCreators

一个函数,用来创建action,可以把业务逻辑隐藏在action里面,或者把一些异步的逻辑隐藏在它里面.
应用场景:如果有多个事件或场景下,需要共用同一逻辑,就可以封装在actionCreators里,比如点击时直接提交这个antionCreator就可以了。

let increaseAction = (val)=>{
    type : "INCREASE",
    val 
}

increase.onclick= ()=> ({
    store.dispatch(increaseAction(8))
})
// 旧的写法
increase.onclick= ()=> {
  store.dispatch({
    type : "INCREASE" ,
    val : 8
  })
}

boundActionCreators

绑定好的action creators,绑定好了dispatch

//绑定dispatch
let boundIncreace = (val)=>{
    store.dispatch({
        type  : "INCREASE",
        val
    })
}
decrease.onclick = ()=>{
    boundIncreace(8)
}
//旧的写法
   decrease.onclick=()=>{
    store.dispatch({
      type : 'DECREASE',
      val : 5
    })
  }

中间件

概念:

要做一件事情=》中间件=》完成一件事情

只要发起action,一切都尘埃落定了,就会执行dispath的逻辑,绝对不会下一秒执行,发起了,但是不执行也是不可能的。有没有一种办法,是在发起之后,再决定是否执行呢?这就需要引入中间件了。(注意 : 不应该在reducer中判断。)
也就是dispath里面可以写一个对象,也可以写一个函数。原来只能写一个对象,现在有了中间件,就可以写函数了。

react中间件 :

拦截这一次action,来决定什么时候达到reducer,决定要不要到达reducer

react-thunk:

action现在可以是一个对象,还可以是一个函数.
如果是一个对象:直接到达reducer;
如果是函数:执行函数,再决定要不要或何时到达reducer

1-下载

npm install --save redux-thunk

2-引入

import {
    createStore,
    applyMiddleware
} from 'redux'
import thunk from 'redux-thunk' 

3-applyMiddleware函数中传入middleware)

// 如果有默认state值,放在第二个参数即可
let store = createStore(counter,applyMiddleware(thunk))
// 接收一个函数为返回值,参数是dispatch和getState
let increaseByOddAction = (val)=>(despatch,getState)=>{
    if(getState()%2!==0){
        dispatch(increaseAction(6))
    }
}

完整计算器案例

import React,{Component} from 'react'
import ReactDOM from 'react-dom'
import {
    createStore,
    applyMiddleware
} from 'redux'

//引入中间件
import thunk from 'redux-thunk' 

const increase = document.querySelector('.increase')
const decrease = document.querySelector('.decrease')
const odd = document.querySelector('.odd')
const async = document.querySelector('.async')
const inputVal = document.querySelector('.val')

//注册状态
let store = createStore(jsq,0,applyMiddleware(thunk))

// actionCreators
// +1
let jia = (val)=>({
    type : "JIA1",
    val
})
// -1
let jian = (val)=>({
    type : "JIAN1",
    val
})

// 提交actionCreators
// +1
let jiaDispatch = (val)=>{
    store.dispatch(jsj(val)) 
}
// -1
let jianDispatch = (val)=>{
    store.dispatch(jsjian(val))
}

// 偶数加
let jsj = (val)=>(dispatch,getState)=>{
    if(getState()%2 !== 0){
        dispatch(jia(val))
        console.log(22)
    }
}
// 基数减
let jsjian = (val)=>(dispatch,getState)=>{
    dispatch(jian(val))
}

// 点击按钮提交dispatch
// 加号按钮
increase.onclick = ()=>{
    jiaDispatch(1)
}

// 减号按钮
decrease.onclick = ()=>{
    jianDispatch(1)
}

odd.onclick = ()=>{
    jiaDispatch(4)
}
async.onclick = ()=>{
    jianDispatch(8)
}
// 订阅action
store.subscribe(()=>{
    let count = store.getState()
                
    console.log(count)
    inputVal.value = count
})

// reducer 
function jsq(state,action){
    let {type,val} = action
    switch(type){
        case "JIA1" : 
            return state+val
            break;
        case "JIAN1" : 
            return state-val
            break;
        default : 
            return state
    }
    return state
}


定义单独的reducer文件 : index.reducer.js

![屏幕快照 2018-06-25 下午2.35.10.png](https://upload-images.jianshu.io/upload_images/8805811-44e6e7924998a113.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

推荐阅读更多精彩内容