什么是Redux,如何使用?

本文深入讲解Redux的基础使用方法,包括其必要性和基本流程,并介绍React-Redux插件简化流程的方式及Redux-Thunk实现异步编程的技术要点。

最近学习了Redux这个React中的数据管理库,有了一些心得。特此记录~

 1.什么是Redux?什么时候我们需要它?

  1.    redux是一个专门用于做数据状态管理的JS库,众所周知,由于React是单向数据流,即父组件向子组件可以传递数据,但是反过来就不行。如果我们想要获得兄弟组件的数据,就需要先访问它们共同的父组件。给我们带来了很大的麻烦,于是redux应运而生。
  1.  尽量不用redux,因为redux有一套非常规范的使用流程或者说是模板,给我们带来了记忆上面的成本。
  2.  某个组件的状态需要让所有的组件都能比较轻松访问到才考虑使用。
  3. 一个组件需要改变全局状态或者说一个组件需要改变另一个组件的状态时我们才考虑使用。

 2.redux使用流程以及常用api

     喜闻乐见地,让我们先上一张很常见的redux流程图。(虽然我知道很难看懂,让我慢慢解释)

redux流程图

   首先在这个五颜六色的图里面,最必要的其实只有三个。也就是 ReactComponent(React组件),Store(存放数据的仓库),Reducers(处理各种业务逻辑的仓库管理员,只有管理员才知道怎么处理各种订单),那么就让我们先来看看一个最简单的redux流程要怎么跑通,在这个过程中再解释这些都是干啥的。

  •  我们使用redux的目的,是想要有一个公共的仓库,那里面保存了一些我们想要共享的数据,使得它们可以方便地存储。既然如此我们就要先创建一个新的仓库出来。

     

npm install --save redux
  •   通过这一段儿代码,我们用npm包管理工具下载下来了redux库,并让它做为我们的运行时依赖。下一步就是新建一个store仓库了。

    

redux_2

   一般来说,我们需要新建一个文件夹来保存我们的redux文件,在这里我取名为store,在这个文件夹里面新建一个index.js文件,是用来创建store仓库用的。

  •     新建Store的代码

   


// /Store/index.js
import React from 'react';
import {createStore} from 'redux';


const store = createStore(//这里需要一个reducer管理员啦);


export default store;
  •    这个时候就引出了store的第一个方法,createStore()--我们可以见名知义地知道这个方法就是用来创建仓库的,一般来说仓库需要一个仓库管理员,这里我们的管理员就是reducer,只是这里我们还没有创建它,接下来我们就来看reducer如何创建并初始化。

 

// store/reducer.js

// 在reducer这个管理员中处理并返回的state会自动修改仓库中的state
// 在这里不允许直接修改state 我们必须要先拷贝出一个副本 在这个副本上修改好了
// 再返回给store最终修改里面的状态 这样子可以防止我们随意操作store里面的数据
// 从而带来一些不可控的后果
// state一旦初始化一次并返回给store后 我们就可以认为后面的state就是store里面的数据

const defaultState = {};
export default (state = defaultState,action)=>{

    return state;
}
  •   这样就完成一个仓库管理员reducer的创建和初始化,可以看到我们的管理员其实就是一个函数,它默认接受两个参数。
  1.  你需要管理什么数据呀(state)?刚开始总得给我指定一个默认的吧(defaultState)
  2.  如果你要操作我仓库里面的数据的话总得告诉我你想做什么操作(action),这个操作有没有特殊要求吧(action)  

    正所谓巧妇难为无米之炊,我们刚才已经创建了仓库,也有了仓库管理员,接下来就要把仓库管理员放到仓库里面啦~ 

     

// /Store/index.js
import React from 'react';
import {createStore} from 'redux';
//引入了reducer
import reducer from './reducer';

//传入了reducer
const store = createStore(reducer);


export default store;

     可以看到,这里比上面只多了两句代码,做的事情也就是把仓库管理员reducer放入仓库里面,让它开始干活儿~

     可以说,到了这里。我们仓库有了,管理员也有了。顺理成章地, 我们得试试看它哥俩管不管用啊,那么接下来我们的ReactComponent就该上场啦,ReactComponent啊它是个客户,它可以对仓库发出"订单",这个订单其实就是上面reducer里面的action,action其实是个对象,至于它里面应该有啥,我们待会儿再说。

  •  假设我们现在正在做一个待办列表(TodoList)的应用,我们以增加待办项的代码为例子来解释。

    

// ./TodoList.js
/*
   在这个例子里面的defaultSate={

        inputValue:"",
        list:['学数学','学英语']
 }

*/

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


export default class TodoList extends React.Component{

       constructor(props){
            super(props);
            // 对该组件的state赋初值
            // store.getState();此方法调用store里面目前的数据
            this.state = store.getState();
            
      }
handleAdd = ()=>{

      const action = {
            type: 'add_item',
     }
  // store的方法 意为"发送订单"
  // 即"客户"ReactComponent给这个store发送订单
  // 仓库本身无法处理 我们需要仓库管理员来处理
  // 这也是我们后面要说到的一个重点
   store.dispatch(action);
}
     render(){
            return (
                    <div>
                            ...
                            <button onClick={this.handleAdd}>增加</buttton>
                    </div>
             )
      }

}
 

  可以看到我们的"客户"ReactComponent,通过触发事件的形式来给store发订单,国有国法,家有家规。我们写"订单"的时候一定得按照一定的规则去写,这样子会方便管理我们可以视action为这个订单,了解一下我们写订单都要遵循什么规范吧~

  1.   必须有一个type属性,属性值是字符串。这个属性的目的就是告诉reducer管理员你要进行什么操作,写什么可以自己定义。只要做到见名知义即可。
  2.  第二个属性是完成这个操作需要的数据,比如想要删除待办项中的一条总得告诉reducer要删除的是哪一项吧,这个时候就需要要删除待办项的索引了。

  只要按照这两个标准写出的action"订单"就是一个合格的"订单",有了订单我们就可以给这个仓库发订单了,要发订单我们使用的是store.dispatch(订单)方法,dispatch在英语里面的意思就是派遣,派发的意思,里面需要传入的你需要派发的订单action,这样子我们在前台做的工作就完成了,我们写好了订单,也给仓库发出了订单,下一步就看仓库那边儿给我们的回应了。

  仓库要做的工作:仓库本身是不能处理订单的,我们需要让管理员来处理,所以这个时候我们的reducer就上场了:

// ./store/reducer.js

import React from 'react';

// 仓库的默认初始状态
const defaultState = {};

// 既然仓库管理员redcuer需要处理订单
// 那么就需要两个值 一个是仓库在订单来之前的状态state
// 另一个就是我们的订单了
export default (state=defaultState,action)=>{

  //在这里我们处理前面发过来的订单
  if(action.type ==== 'add_item'){
    // 拷贝一个副本出来 我们作为管理员要先在这个副本上确定好我们要修改什么
    // 最后再返回给store修改真正的数据
    // 这个代码是最常见的深拷贝对象一种办法
    let newState = JSON.parse(JSON.stringify(state));
    // 在指定的索引处删除一个元素
    newState.splice(action.index,1);
   // 最后再返回修改好的状态 到store中自动修改原来的状态
    return newState;
  }   
   


  // reducer中只要有返回值 就会修改store里面的状态
  // 但是不能在这里面自己修改store的值
  return state;

}
  •   可以看到上面的代码中通过处理前面发来的订单,"仓库管理员"reducer返回了修改好的新状态,通过这种方式修改了仓库中的数据,到这里"客户"发订单,"管理员"处理订单,最后结果影响仓库的流程就差不多走完了,当然还有最后一步,当状态被修改之后,"客户"要能接收到这个变化。
// ./TodoList.js
/*
   在这个例子里面的defaultSate={

        inputValue:"",
        list:['学数学','学英语']
 }

*/

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


export default class TodoList extends React.Component{

       constructor(props){
            super(props);
            // 对该组件的state赋初值
            // store.getState();此方法调用store里面目前的数据
            this.state = store.getState();
             // 仓库变化时会调用的函数
            this.storeChange = this.storeChange.bind(this);
            // 监听仓库的变化
            store.subscribe(this.storeChange);
           
            
      }
     
      storeChange(){
         
         // this.setState()方法会调用类里面的render方法
        // 页面重新渲染
         this.setState(store.getState());
   }

}
 
  •    store.subscribe(),subscribe在英语里面就是订阅,监听的意思,所以这个函数会在store发生变化时调用你传入的参数,你可以传入一个函数,让store改变时调用它,这个时候一般是调用render函数 或者 自定义一个函数在里面调用setState() 这个方法会自动调用render()。

  2. React-Redux---Redux的简化版本

   刚才的一整套流程下来虽然很规范,但是总归是流程略显复杂了。给我们记忆上带来不小的麻烦,针对于这样的困扰,有识之士们开发出了一个新的React插件库,React-redux

   特点:

  1. 一个 react 插件库
  2. 专门用来简化 react 应用中使用的 redux

  在React-redux中提供了一个新标签<Provider>和一个新函数 connect,我们来看看他们都有什么作用:

  • Provider:让所有组件都可以得到 state 数据 
import {Provider} from 'react-redux'

<Provider store={store}>
  <App />
</Provider>
  • connect():用于包装 UI 组件生成容器组件

   

import {connect} from 'react-redux'

// 用connect包装Counter组件再返回出去
export default connect(
  mapStateToprops, // 是个回调函数,将状态映射成属性,返回对象
  mapDispatchToProps // 是个对象,包含actions中的方法(将在内部被转换成调用dispatch的函数)
)(Counter) // 此处填入需要connect函数管理的组件 会映射到这个组件的props上


// mapStateToprops() 将store中的state映射到组件的props上
// mapDispatchToprops 将actions中的对象传入connect内部自动帮我们转成一个调用了dispatch的函数,最后再映射到props上,方便我们调用。
  •  下面我们看一个写法:

 

import {connect} from 'react-redux';
import {addItem} from './store/actions';

class TodoList extends React.Component{

   handleAdd(){

        this.prpos.addItem();
  }
  render(){

    return (
       <button onClick={this.handleAdd}/>
  )
 }

}

export default connect(
   
    // 此处是个函数 默认被传入了store中的state
    (state) =>{ inputValue:state.inputValue },
    // 此处是个函数 默认需要传入action对象 内部帮我们转换成函数,并自动调用dispatch函数
    {addItem}

)
  •    上面就是一个常见的react-redux写法,它实际上运用provider和connect,将视图层和业务逻辑分开了,也简化了使用redux的流程,不用我们再一个个调用dispatch函数。

 3.Redux异步编程--Redux-Thunk

 redux-thunk 增强了actions,以前的actions只能是对象,现在可以是一个函数,那么就可以在这个函数里面写业务逻辑了,比如说异步请求。redux-thunk,是redux的中间件, 中间件的作用我们可以理解为是增强这个库的功能。

  1.thunk的配置

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

const store = createStore(
  counter,
  applyMiddleware(thunk) // 应用异步中间件
)



// 如果在浏览器中用了 redux-devTools这个插件 那么如果你想要继续使用 需要额外添加一个增强函数
import { createStore , applyMiddleware ,compose } from 'redux'  //  引入createStore方法
import reducer from './reducer'    
import thunk from 'redux-thunk'

const composeEnhancers =   window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose

const enhancer = composeEnhancers(applyMiddleware(thunk))

const store = createStore( reducer, enhancer) // 创建数据存储仓库
export default store   //暴露出去

   2.应用thunk编写异步程序

  

export const incrementAsync = (number) => {
  return dispatch => { // 异步action会返回一个函数
    // 异步的代码必须被封装到action中
    setTimeout(() => {
      // 1s后才去分发一个同步的action(dispatch一个同步action)
      dispatch(increment(number))
    }, 1000)
  }
}
  •  可以看到 thunk为我们封装了dispatch方法这个时候我们可以直接调用。

 4.总结

   总共讲了三点 

  1.     redux使用方法。
  2.   react-redux这个简化redux的插件的使用办法。
  3.   redux-thunk 这个redux中间件的使用方法。

   

   

 

   

 

 

 

 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值