React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)

1、前言

聊起React,就不得不提redux,虽然并不是每个项目中必须使用redux,但是作为一个新生代农民工(国家认证),必须对相关概念了解,这样使用的时候才能立马上手,最近的项目中我们没怎么使用redux,为了防止遗忘,就琢磨写下一些学习心得,做到温故知新。

2、传统MVC框架

先看张图
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第1张图片MVC的全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范。

VView视图,是指用户看到并与之交互的界面

MModel模型是管理数据,是很多业务逻辑都在模型中完成。在MVC的三个部件中,模型拥有最多的处理任务。

CController控制器,是指控制器接受用户的输入并调用模型和视图去完成用户的需求。控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。

看似MVC框架的数据流很理想,请求先到Controller, 由Controller调用Model中的数据交给View进行渲染,但是在实际的项目中,又是允许Model和View直接通信的。然后就出现了这样的结果,如下图:
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第2张图片

3、Flux

React只是一个MVC中的V(视图层)只管页面中的渲染,一旦有数据管理的时候,React本身的能力就不足以支撑复杂组件结构的项目,在传统的MVC中,就需要用到Model和Controller

Facebook对于当时世面上的MVC框架并不满意,于是就有了Flux, 但Flux并不是一个MVC框架,他是一种新的思想。

在2013年,Facebook让React亮相的同时推出了Flux框架,React的初衷实际上是用来替代jQuery的,Flux实际上就可以用来替代Backbone.jsEmber.js等一系列MVC架构的前端JS框架。

其实FluxReact里的应用就类似于Vue中的Vuex的作用,但是在Vue中,Vue是完整的mvvm框架,而Vuex只是一个全局的插件。
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第3张图片

  • View: 视图层
  • ActionCreator(动作创造者):视图层发出的消息(比如mouseClick)
  • Dispatcher(派发器):用来接收Actions、执行回调函数
  • Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面

Flux的流程:

  1. 组件获取到store中保存的数据挂载在自己的状态上

  2. 用户产生了操作,调用actions的方法

  3. actions接收到了用户的操作,进行一系列的逻辑代码、异步操作

  4. 然后actions会创建出对应的action,action带有标识性的属性

  5. actions调用dispatcher的dispatch方法将action传递给dispatcher

  6. dispatcher接收到action并根据标识信息判断之后,调用store的更改数据的方法

  7. store的方法被调用后,更改状态,并触发自己的某一个事件

  8. store更改状态后事件被触发,该事件的处理程序会通知view去获取最新的数据

4、Redux

4.1、redux的介绍

React 只是 DOM 的一个抽象层,并不是 Web 应用的完整解决方案。有两个方面,它没涉及。

  • 代码结构
  • 组件之间的通信

2013年 Facebook 提出了 Flux 架构的思想,引发了很多的实现。
2015年,Redux 出现,将 Flux 与函数式编程结合一起,很短时间内就成为了最热门的前端架构。

如果你不知道是否需要 Redux,那就是不需要它

只有遇到 React 实在解决不了的问题,你才需要 Redux

简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。

不需要使用Redux的项目:

  • 用户的使用方式非常简单
  • 用户之间没有协作
  • 不需要与服务器大量交互,也没有使用 WebSocket
  • 视图层(View)只从单一来源获取数据

需要使用Redux的项目:

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

从组件层面考虑,什么样子的需要Redux:

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

4.2、redux的设计思想:

  1. Web 应用是一个状态机,视图状态一一对应的。
  2. 所有的状态,保存在一个对象里面(唯一数据源)
注意:flux、redux都不是必须和react搭配使用的,因为flux和redux是完整的架构,在学习react的时候,只是将react的组件作为redux中的视图层去使用了。

4.3、redux的使用的三大原则:

  • Single Source of Truth(唯一的数据源)

  • State is read-only(状态是只读的)

  • Changes are made with pure function(数据的改变必须通过纯函数完成)

4.4、原生模拟实现redux

reducer.js文件

纯函数:
1:相同的入参,得到相同的输出 
2:不能修改入参
const reducer = ( state , action ) => {
     
    action = action ||{
      type : ''}
    switch(action.type){
     
        case 'increment':
            return {
     
                ...state,
                count : state.count + 1
            }
        case " decrement":
            return {
     
                ...state,
                count : state.count - 1
            }
        default:
            return state
    }
}

export {
     
    reducer
}

store.js文件

import {
      reducer } from './reducer'

let state = {
     
  count: 0
}

const createStore = () => {
     
  // getState 获取状态
  const getState = () => state
  
  // 观察者模式
  const listeners = []
  // subscribe 订阅
  const subscribe = listener => listeners.push(listener)
  
  const dispatch = action => {
     
    // console.log(reducer(state, action))
    state = reducer(state, action)
    // publish 发布
    listeners.forEach(listener => listener())
  }

  return {
     
    dispatch,
    getState,
    subscribe
  }
}

const store = createStore()

const render = () => {
     
  document.querySelector('#count').innerHTML = store.getState().count
}

store.subscribe(render)

export default store

App.js某任意组件

import React, {
      Component } from 'react'
import store from './Store'

 class App extends Component {
     
     componentDidMount(){
     
         store.dispatch()
     }
    render() {
     
        return (
            <div >
                <button onClick={
     store.dispatch.bind(this,{
     type : 'increment'})}>+</button>
                <span id='count'></span>
                <button onClick={
     store.dispatch.bind(this,{
     type : 'decrement'})}>-</button>
            </div>
        )
    }
}
export default App

4.4.1、整体思路

(1)在store文件里面定义createStore()方法,createStore方法包含三种方法:

  • getState:通过reducer改变state状态,并获取最新state状态
  • subscribe:发布订阅模式,通过subscribe来订阅,即用一个空数组来push方法
  • dispatch:调用action,里面包含两个操作,一个是调用reducer(state,action)纯函数来获得最新的state;一个是publish发布,把刚才通过空数组subscribe订阅的方法数组,forEach遍历运行,即完成所谓的发布。

所以每dispatch一次,就会改变一次状态;另外会publish发布一次刚才subscribe订阅的东西。所以createStore最后return出去的是dispatch,getState,subscribe。

在createStore外面可以定义render函数首次的订阅subscribe,并且在store文件最开始的时候引入reducer。

(2)在reducer(state,action)纯函数里面纯函数的特点:1、相同的入参,得到相同的输出 2、函数的输出结果不能对入参做解构修改,通过action.type来区分,进而执行不同的操作,返回一个新的state。

(3)在某个组件里面引入store文件,通过调用store.dispatch,并传入不同的type类型,进行不同操作。

4.5、细说redux

安装redux

yarn add redux

4.5.1、redux里面有什么

在安装好redux之后,可以随便创建一个js文件,在里面导入require刚刚安装的redux,并打印,可以看到redux里面都包括什么东西
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第4张图片
可以看出,redux里面包含:

  • _DO_NOT_USE__ActionTypes对象
  • applyMiddleware
  • bindActionCreators
  • combineReducers
  • compose
  • createStore
    这些方法(下文细讲)

4.5.2、store里面有什么

我们从redux里面解构出createStore,将写好的reducer作为参数传入其中,打印查看

reducer.js文件

const defaultState = {
     count: 0}
const reducer = (state = defaultState, action) => {
     
    switch(action.type) {
     
        case 'increment':
            return {
     
                ...state,
                count: state.count + 1
            }
        case 'decrement':
            return {
     
                ...state,
                count: state.count - 1
            }
        default:
            return state
    }
}
module.exports = reducer

React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第5张图片
可以看出,如之前我们自己用原生模拟的redux一样,这次的store里面包含:

  • dispatch
  • subscribe
  • getState
  • replaceReducer
  • observable
    等方法

store.dispatch()
还是刚才的reducer文件,这次我们store.dispatch()派发一个type类型,并且打印store.getState(),看看里面有什么
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第6张图片
调用几次就改变几次
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第7张图片
store.dispatch()store.subscribe()

subscribe相当于订阅,而每一次dispatch相当于发布,这不同于vue中的 object.defineproperty()
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第8张图片

4.6、react+redux实现TodoList

安装react-redux :提供Providerconnect (注意区分reduxreact-redux的区别)

yarn add react-redux

react-redux里面有:

  • mapStateToProps:映射state到当前组件的props上面
  • mapDispatchToProps:映射某个方法到当前组件的props上面,该方法里面通过dispatch派发事件。

mapStateToPropsmapDispatchToProps都是通过return返回一个对象,在对象里面拿到数据和函数

(附上一张redux的流程图)
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第9张图片

4.6.1、基础版(无Action Creater和Middleware版)

外部入口index.js文件

react-redux里面解构出Provider,然后引用store,将store注入到Provider里面,这样就可以在使用的时候,通过connect( mapStateToProps ,mapDispatchToProps)来获取到写在props上面的state各种方法

import React from 'react'
import ReactDOM from 'react-dom'
import {
      Provider } from 'react-redux'

import App from './13-redux/04-todolist/TodoList'
import store from './13-redux/04-todolist/store/index'
ReactDOM.render(
    <Provider store={
     store}>
        <App ></App>
    </Provider>,
    document.querySelector('#root')
)

TodoList文件:

import React, {
      Component } from 'react'
import Form from './Form'
import List from './List'
//引入两个单独的组件
class App extends Component {
     
    render() {
     
        return (
            <div>
               <Form></Form>
               <List></List>
            </div>
        )
    }
}

export default App

List文件

import React, {
      Component } from 'react'
import {
      connect } from 'react-redux'

//映射state到当前组件的props上面
const mapStateToProps = ( state) => {
     
    return{
     
        list : state.list
    }
}

//映射dispatch到当前组件的props上面
const mapDispatchToProps = (dispatch) => {
     
    return {
     
        deleteData: (index) => {
     
            dispatch({
     
                type : 'DELETE_DATA',
                index
            })
        }
    }
}
@connect(mapStateToProps,mapDispatchToProps)
class List extends Component {
     
    componentDidMount(){
     
        console.log('this' ,this)
    }
    handleClick = (index) => {
     
        return()=>{
     
            this.props.deleteData(index)
        }
    }
    render() {
     
        return (
            <ul>
                {
     
                    this.props.list.map((value ,index)=>{
     
                        return(
                            <li key={
     index}>
                                {
     value}
                                <button onClick={
     this.handleClick(index)}>删除</button>
                            </li>
                        )
                    })
                }
            </ul>
        )
    }
}

export default List 

Form文件

import React, {
      Component } from 'react'
import {
      connect } from 'react-redux'

//映射state到当前组件的props上面
const mapStateToProps = () => {
     
   return{
     }
}
//映射dispatch到当前组件的props上面
const mapDispatchToProps = (dispatch) => {
     
    return {
     
        putData: (task) => {
     
            dispatch({
     
                type : 'PUT_DATA',
                task
            })
        }
    }
}
@connect(null,mapDispatchToProps)
 class Form extends Component {
     
    state = {
     
        task : ''
    }
    handleChange = (e) =>{
     
        this.setState({
     
            task : e.target.value
        })
    }
    handleKeyUp = (e) => {
     
        if(e.keyCode === 13){
     
            // console.log(this)
            this.props.putData(this.state.task)
            this.setState({
     
                task : ''
            })
        }
    }
    handleClick = () => {
     
        this.props.putData(this.state.task)
        this.setState({
     
            task : ''
        })
    }
    render() {
     
        return (
            <div>
                <input 
                    type="text" 
                    name="" id=""
                    value={
     this.state.task}
                    onChange={
     this.handleChange}
                    onKeyUp={
     this.handleKeyUp}
                />
                <button onClick={
     this.handleClick}>确定</button>
            </div>
        )
    }
}
export default Form

store下面的index

这里的主要目的是引入reducer,对外抛出store

//从react-redux里面解构出createStore
import {
      createStore } from 'redux'
import reducer from './reducer'
//将reducer引入createStore里面
const store = createStore(reducer)
export default store

store下面的reducer

reducer是一个纯函数,所以defaultState是个对象,reducer里面不管哪一个type类型,返回出来的都应该是对象类型,这样才能保证传入相同,传出也相同

const defaultState = {
     
    arr:[
        '苹果','香蕉','葡萄'
    ],
    list : [
        '任务一' , '任务二'
    ]
}

const reducer = ( state = defaultState,action) => {
     
    switch(action.type){
     
        case 'LOAD_DATA':
            return state;
        case 'PUT_DATA':
            return {
     
             //...state是为了防止defaultState里面除了list数组之外还有别的属性
                ...state,
                list : [
                    ...state.list,
                    action.task
                ]
            }
        case 'DELETE_DATA':
            let list1 = state.list.filter((v,i)=>{
     
                return i!==action.index
            })
            return {
     
                //...state是为了防止defaultState里面除了list数组之外还有别的属性
                ...state,
                list : list1
            }
        default :
            return state
    }
}
export default reducer

效果图
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第10张图片
实现原理

  • 开始时,List文件里面打印this,发现deleteDatalist已经挂载在props上面,这个功劳属于从react-redux里面解构的Providerconnect
  • 在Form文件里的输入框中输入值,点击‘确定’,触发挂载在propsputData函数,派发dispatch一个typePUT_DATA类型的函数,task参数即为输入的内容,然后store里面的reducer通过识别action.type类型,将action.task存入list当中,这样就完成了添加
  • 在List文件里面识别挂载在propslist数据,进行渲染;点击‘删除’,触发挂载在props上面的deleteData函数,派发dispatch一个typeDELETE_DATA类型的函数,index为渲染list的某一项key值,然后store里面的reducer通过识别action.type类型,将list数组中index不等于action.index筛选出来,并重新赋值于list,这样就完成了删除

4.6.2、基础版(有Action Creater和无Middleware版)

从字面意思来讲,Action Creators就是集成所有Action动作的文件,帮助我们抽离出来各种dispatch派发函数

外部入口index.js文件
同4.6.1的部分

TodoList文件:
同4.6.1的部分

List文件

import React, {
      Component } from 'react'
import {
      connect } from 'react-redux'
import {
      deleteAction } from './store/actionCreator'

//映射state到当前组件的props上面
const mapStateToProps = ( state) => {
     
    return{
     
        list : state.list
    }
}
//映射dispatch到当前组件的props上面
const mapDispatchToProps = (dispatch) => {
     
    return {
     
        deleteData: (index) => {
     
            // 引入actionCreator之后
            dispatch(deleteAction(index))
        }
    }
}
@connect(mapStateToProps,mapDispatchToProps)
class List extends Component {
     
    componentDidMount(){
     
        // console.log('this' ,this)
    }
    handleClick = (index) => {
     
        return()=>{
     
            this.props.deleteData(index)
        }
    }
    render() {
     
        return (
            <ul>
                {
     
                    this.props.list.map((value ,index)=>{
     
                        return(
                            <li key={
     index}>
                                {
     value}
                                <button onClick={
     this.handleClick(index)}>删除</button>
                            </li>
                        )
                    })
                }
            </ul>
        )
    }
}
export default List  

Form文件

import React, {
      Component } from 'react'
import {
      connect } from 'react-redux'
import {
      putdataAction } from './store/actionCreator'
//映射state到当前组件的props上面
const mapStateToProps = () => {
     
   return{
     }
}
//映射dispatch到当前组件的props上面
const mapDispatchToProps = (dispatch) => {
     
    return {
     
        putData: (task) => {
     
             //引入actionCreator之后
            dispatch(putdataAction(task))
        }
    }
}
@connect(null,mapDispatchToProps)
 class Form extends Component {
     
    state = {
     
        task : ''
    }
    handleChange = (e) =>{
     
        this.setState({
     
            task : e.target.value
        })
    }
    handleKeyUp = (e) => {
     
        if(e.keyCode === 13){
     
            // console.log(this)
            this.props.putData(this.state.task)
            this.setState({
     
                task : ''
            })
        }
    }
    handleClick = () => {
     
        this.props.putData(this.state.task)
        this.setState({
     
            task : ''
        })
    }
    render() {
     
        return (
            <div>
                <input 
                    type="text" 
                    name="" id=""
                    value={
     this.state.task}
                    onChange={
     this.handleChange}
                    onKeyUp={
     this.handleKeyUp}
                />
                <button onClick={
     this.handleClick}>确定</button>
            </div>
        )
    }
}
export default Form

store下面的index
同4.6.1的部分

store下面的reducer
同4.6.1的部分

store下面的actionCreator

将aciton集成在一个文件,统一派发dispatch,便于管理。

const deleteAction = (index) => {
     
    return {
     
        type : 'DELETE_DATA',
        index
    }
}

const putdataAction = (task) => {
     
    return {
     
        type : 'PUT_DATA',
        task
    }
}

export {
     
    deleteAction,
    putdataAction
}

效果图
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第11张图片

4.6.3、升级版(有Middleware版)

4.6.3.1、 什么是Middleware

在4.6.1中,我们实现了简单版的react+redux,但是实际开发工作中,我们往往会就有一些异步操作,比如ajax请求,这时上面的基础写法就满足不了我们的业务需求,所以我们需要引入一些可以满足异步操作的工具,即中间件Middleware(能够满足异步操作的要求)

下图是一张react+redux的流程图:
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第12张图片
同时红点标出的地方就是我们进行异步操作的位置。原因是:

  • store相当于一个聚合容器,不做细节的代码操作
  • reducer相当于纯函数不能进行有副作用的操作
  • React Components组件里面可以做异步操作,进行同步派发,但是这样就表明redux是依赖于React,这跟redux是独立于React的原则相违背
  • Action Creator就是简单派发一个对象,不适合做

所以我们只能在Action Creaters后进行异步操作

4.6.3.2 、不使用Middleware

假设没有引用中间件,然后我们在actionCreator里面将扁平(plain)对象改为外面的多套一层函数,这就意味着在组件中dispatch派发的不是一个扁平的对象,而是一个异步的函数,里面包着扁平的对象,如图所示:
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第13张图片
List文件里面添加测试代码:
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第14张图片
其他文件保持不变,然后启动运行
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第15张图片
发现控制台报出错误,提示我们如果直接在Action Creator后进行异步操作不能成功,因为Actions最后必须是扁平对象,所以我们必须使用中间件Middleware来帮助我们。

4.6.3.3、 使用Middleware

常用的中间件有redux-thunkredux-saga等,这里我们引入redux-thunk中间件

安装redux-thunk

yarn add redux-thunk

store下面的index

redux中解构出applyMiddleware,引入thunk,传入createStore

import {
      createStore ,applyMiddleware} from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'
//中间件一旦挂上,组件中dispatch 就必然会被中间件拦下来
const middleware = applyMiddleware(thunk)
const store = createStore( reducer , middleware)
export default store

其他文件保持不变,再次运行程序,页面可以正常展示
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第16张图片
所以我们可以看出,是中间件Middleware帮助我们去运行异步操作

4.6.3.4、 使用Middleware进行异步ajax请求

场景:假定页面最开始就要展示从ajax请求回来的数据(我们利用fetch模拟),同时还要可以手动添加和删除。

外部入口index.js文件

react-redux里面解构出Provider,然后引用store,将store注入到Provider里面,这样就可以在使用的时候,通过connect( mapStateToProps ,mapDispatchToProps)来获取到写在props上面的state各种方法

mport React from 'react'
import ReactDOM from 'react-dom'
import {
      Provider } from 'react-redux'
import App from './13-redux/05-todolist2/App'
import store from './13-redux/05-todolist2/store/index'
ReactDOM.render(
    <Provider store={
     store}>
        <App ></App>
    </Provider>,
    document.querySelector('#root')
)

TodoList文件:

import React, {
      Component } from 'react'
import Form from './Form'
import List from './List'
//引入两个单独的组件
class App extends Component {
     
    render() {
     
        return (
            <div>
               <Form></Form>
               <List></List>
            </div>
        )
    }
}
export default App

List文件

import React, {
      Component } from 'react'
import {
      connect } from 'react-redux'
import {
      
    deleteAction ,
    setdataAction
} from './store/actionCreator'

//映射state到当前组件的props上面
const mapStateToProps = ( state) => {
     
    return{
     
        list : state.list
    }
}
//映射dispatch到当前组件的props上面
const mapDispatchToProps = (dispatch) => {
     
    return {
     
        deleteData: (index) => {
        
        	//没有引入actionCreator之前
            // dispatch({
     
            //     type : 'DELETE_DATA',
            //     index
            // })
  
            // 引入actionCreator之后
            dispatch(deleteAction(index))
        },
        setData : ()=>{
     
            dispatch(setdataAction())
        },
    }
}
@connect(mapStateToProps,mapDispatchToProps)
class List extends Component {
     
    componentDidMount(){
     
    	//场景规定刚开始就展示列表数据
        this.props.setData()
	}
    handleClick = (index) => {
     
        return()=>{
     
            this.props.deleteData(index)
        }
    }
    render() {
     
        return (
            <ul>
                {
     
                    this.props.list?.map((value ,index)=>{
     
                        return(
                            <li key={
     index}>
                                {
     value.positionName}
                                <button onClick={
     this.handleClick(index)}>删除</button>
                            </li>
                        )
                    })
                }
            </ul>
        )
    }
}
export default List 

Form文件

import React, {
      Component } from 'react'
import {
      connect } from 'react-redux'

import {
      putdataAction } from './store/actionCreator'

//映射state到当前组件的props上面
// const mapStateToProps = () => {}

//映射dispatch到当前组件的props上面
const mapDispatchToProps = (dispatch) => {
     
    return {
     
        putData: (task) => {
     
            // 没有引入actionCreator之前
            // dispatch({
     
            //     type : 'PUT_DATA',
            //     task
            // })

            //引入actionCreator之后
            dispatch(putdataAction(task))
        }
    }
}

@connect(null,mapDispatchToProps)
 class Form extends Component {
     
    state = {
     
        task : ''
    }
    handleChange = (e) =>{
     
        this.setState({
     
            task : e.target.value
        })
    }
    handleKeyUp = (e) => {
     
        if(e.keyCode === 13){
     
            this.props.putData(this.state.task)
            this.setState({
     
                task : ''
            })
        }
    }
    render() {
     
        return (
            <div>
                <input 
                    type="text" 
                    name="" id=""
                    value={
     this.state.task}
                    onChange={
     this.handleChange}
                    onKeyUp={
     this.handleKeyUp}
                />
            </div>
        )
    }
}
export default Form

store下面的index文件

import {
      createStore ,applyMiddleware} from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'
//中间件一旦挂上,组件中dispatch 就必然会会被中间件拦下来
const middleware = applyMiddleware(thunk)
const store = createStore(reducer , middleware)
export default store

store下面的actionCreator文件

List组件中调用的setdataAction方法是进行异步操作派发非扁平数据,这里是中间件Middleware帮助我们运行return后的箭头函数,并帮我们传入dispatch参数,进行完异步操作之后,用拿到的数据去派发dispatch扁平的action,从而继续调用reducer进行后续操作···

const deleteAction = (index) => {
     
    return {
     
        type : 'DELETE_DATA',
        index
    }
}
const putdataAction = (task) => {
     
    return {
     
        type : 'PUT_DATA',
        task
    }
}
// 派发非扁平数据,中间件Middleware帮助我们run里面那层
const setdataAction = () => {
     
    return dispatch =>{
     
        fetch('./data.json')
        .then( response => response.json()) 
        .then(result => {
     
            dispatch(loaddataAction(result.result))
        })
    }
}
// 派发扁平的数据类型
const loaddataAction = (data) =>{
     
    return {
     
        type : 'SET_DATA',
        data 
    }
}
export {
     
    deleteAction,
    putdataAction,
    setdataAction
}

store下面的reducer文件
我们在reducer里面假如许多console.log,来帮助我们更直观来看一下调用过程

const defaultState = {
     }

const reducer = ( state = defaultState,action) => {
     
    console.log('---action---',action)
    switch(action.type){
     
        case 'SET_DATA':
            console.log('----SET_DATA----',state)
            console.log('----SET_DATA----',action)
            return {
     
                ...state,
                list : action.data
            }
        case 'PUT_DATA':
            console.log('---PUT_DATA---' , state)
            console.log('---PUT_DATA---', action)
            return {
     
                ...state,
                list : [
                    ...state.list,
                    {
     
                        positionName : action.task,
                    }
                ]
            }
        case 'DELETE_DATA':
            console.log('---DELETE_DATA---' , state)
            console.log('---DELETE_DATA---', action)
            let list1 = state.list.filter((v,i)=>{
     
                return i!==action.index
            })
            return {
     
                //...state是为了防止defaultState里面除了list数组之外还有别的属性
                ...state,
                list : list1
            }
        default :
            return state
    }
}
export default reducer

效果图
React学习笔记——状态管理工具redux的基础介绍和使用(从基础概念到具体使用,全网最全)_第17张图片
可以看出:

开始页面未进行任何操作时:

  • 控制台中打印了---action--- {type: "@@redux/INITi.1.2.k.w.c"},这个是redux本身自己帮助我们一开始先执行的(下篇博客讲);
  • List文件里面在componentDidMount里面先派发dispatch一个异步任务setdataAction;
  • 随后在actionCretor文件里面在中间件Middleware作用下拿到fetch异步请求的数据,然后再派发一个扁平数据类型
  • reducer文件识别action.type后,对action.data进行操作,并返回一个对象
  • List文件拿到props中的list数据,进行最初的页面渲染
  • 控制台中打印了state为----SET_DATA---- {},action为----SET_DATA---- {type: "SET_DATA", data: Array(15)}

随后再输入添加某些任务,删除任意任务,逻辑也是相对如此,reducer根据不同的action.type类型,进行不同的操作。

你可能感兴趣的:(React,react.js)