redux、react-redux、redux-thunk、combineReducers 基本使用

510 阅读7分钟

redux是专门做状态管理的独立第3方库,对应用中状态进行集中式管理,与react-redux, redux-thunk 等插件配置使用

react-redux: 主要用于简化react应用中使用redux(react组件和redux进行通信的时候,代码耦合度比较高)提供了两个API

  • Provider(这是一个组件),接收一个store
    <Provider store={store}>
    	<App />
    </Provider>
    
  • connect(将react组件和redux连接起来,进行通信)

Redux理解

什么? redux是专门做状态管理的独立第3方库,不是react插件,但一般都用在react项目中

作用? 对应用中状态进行集中式管理(写/读)

开发: 与react-redux, redux-thunk 等插件配置使用

Redux三个核心的概念

action:

由调用redux仓库的事件发出,可以想象为有人借书,或者管理员往图书馆里添加书,这些操作都会告诉管理员

默认是对象(同步action),{type:'xxx',data:value} 需要通过对应的actionCreator产生
它的值也可以是函数(异步action),需要引入redux-thunk才可以

reducer

借书或者还书的行为,这些动作如何改变图书馆藏书的状态,就需要Reducer来计算,同时反馈给store,这个时候得到的就是新的仓库状态了

根据老的state和指定的action,返回一个新的state
不能修改老的state
createStore(), applyMiddleware(), combineReducers() 常用的/核心的

store

仓库,存储数据的地方,可以把他比喻为一个图书管理员,能提供的是当前藏书的状态

redux最核心的管理对象
内部管理者:state和reducer
提供方法:  getState()读状态数据, dispatch()更新状态数据, subscribe() 监视状态数据的改变

redux 基本使用教程

  1. 搭建项目,使项目能跑起来

  2. 安装redux npm install --save redux

  3. src -> redux 新建 store.js actions.js reducer.js action-types.js

    • store.js redux最核心的管理对象:store

      • import {createStore} from 'redux';
        
        import reducer from './reducer';
        // 创建store对象,内部会第一次调用reducer()得到初始状态值
        export default createStore(reducer); 
        
    • reducer.js 函数模块,根据当前state和指定action返回一个新的state

      • import {INCREMENT,DECREMENT} from './action-types'
        // 管理count状态数据的reducer
        注:一般情况下需要管理什么数据,函数名就叫什么名字
        export default function count(state=2,action) { // 参数是固定的 (state指的就是数据本身) state=2 设置默认值
          switch (action.type) {
            case INCREMENT:
              return state + action.data
            case DECREMENT:
              return state - action.data
            default: 
              return state;  
          }
        } 
        
        (action需要通过 Action Creator(创建Action的工厂函数) ==> 下面的action.js)
        action的结构:标识要执行行为的对象
        type:标识属性,值为字符串,唯一,必要性
        xxx :数据属性,值类型任意,可选属性
        //const action ={ type='INCREMENT',data:2 }
        
    • action-types.js 用来存储常见常量,防止写错

      • export const INCREMENT = 'increment';
        export const DECREMENT = 'decrement';
        
    • action.js 包含n个用来创建action的工厂函数(action creator)

      • import {INCREMENT,DECREMENT} from './action-types'
        
        // 增加的action
        export const incre = (num) => ({type:INCREMENT,data:num});
        
        // 减少的action
        export const decre = (num) => ({type:DECREMENT,data:num}) 
        
  4. 数据已经存储好了,下面看 store 怎么使用:(由于数据比较少,直接在App.js中写的操作)

  5. App.js 获取store中的数据

    • render(){
          const count = this.props.store.getState();
          // 1、这里是从props中获取的,所以需要 通过 index.js将 store传递过来
          // 2、调用store.getState() 方法获取
      }
      
  6. 调用 action.js 中的方法,操作(改变)store中的数据

    • App.js
      1、引入 action.js
      import {incre,decre} from './redux/actions';
      
      2、在 App.js相关的方法中,调用action中的方法
       increment = ()=> {
          const number = this.numberRef.current.value * 1;
          this.props.store.dispatch(incre(number)); // 调用action中的方法,改变store中的数据
        }
      
  7. index.js 将 store 绑定到 props 上

/**
 * 入口JS
 */
import React from 'react';
import ReactDom from 'react-dom';
import App from './App';
import store from './redux/store';
ReactDom.render(<App store={store} />,document.getElementById('root'));
  1. store中数据的改变,不会触发组件的更新

    // 默认情况下 store中的数据发生改变,不会触发组件的更新
    store.subscribe(() => { // store内部的状态数据发生改变时的回调
    // 重新渲染APP组件标签
    ReactDom.render(<App store={store} />,document.getElementById('root')); 
                    
    所以在入口 js index.js 中 添加下面的代码               
    

react-redux 介绍

一个react插件库,专门用来简化react应用中的redux

react-redux将所有组件分为两大类

UI组件

a:只负责UI的呈现,不带有任何业务逻辑

b:通过props接收数据(一般数据和函数)

c:不使用任何redux的API

d:一般保存在 components 文件夹下

容器组件

a:负责管理数据和业务逻辑,不负责ui的呈现

b:使用redux的API

c:一般保存在container文件夹下

react-redux 基本使用

该教程是基于👆redux,在redux的基础上进行下面的操作

react-redux语法功能分析

  • react-redux 向外暴露了两个API

    a:Provider组件类

    Provider 组件接收 store 属性,让所有组件都可以看到store,从而通过store读取/更新状态
    

    b:connect函数

    接收两个参数:mapStateToProps 和 mapDispatchToProps

    mapStateToProps:为一个函数,用来指定向UI组件传递哪些一般属性

    mapDispatchToProps:为一个函数或对象,用来指定向UI组件传递哪些函数属性

    connect 执行的返回值为一个高阶组件,包装UI组件,返回一个新的容器组件,容器组件会向UI组件传入前面指定的一般/函数类型属性

<Provider store={store}>
    connect(
	    state=({xxx.state.xxx})
	    {actionCreator1,actionCreator2},
    )(UI组件)    
</Provider>
产生的就是容器组件,负责向UI组件传递标签属性,
一般属性值从state中获取,函数属性内部会执行dispatch分发action
  1. 安装 npm install --save react-redux
  2. 删除 src -> App.js, 在 src -> 新建 container -> App.js, 在 src 新建 components -> Counter.js
  3. Counter.js 主要是 UI界面的呈现

特别注意:redux中的数据和方法获取,是直接从 props 中获取的

import React, { Component } from 'react';
import PropTypes from 'prop-types';
/**
 * UI组件
 */
class Counter extends Component {
  static propTypes = {
    count: PropTypes.number.isRequired,
    incre: PropTypes.func.isRequired,
    decre: PropTypes.func.isRequired
  } 
  constructor(props) {
    super(props);
    this.numberRef = React.createRef();
  }
  increment = ()=> {
    const number = this.numberRef.current.value * 1;
    this.props.incre(number);
  }
  decrement = ()=> {
    const number = this.numberRef.current.value * 1;
    // 调用redux中的方法,操作redux中的数据
    this.props.decre(number);
  }
  render() { 
    // 获取 redux 中的 count
     const count = this.props.count;
    return (
      <div>
        <p>click {count} times</p>
        <div>
          <select ref={this.numberRef}>
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
            <option value="4">4</option>
            <option value="5">5</option>            
          </select>
          <div className="btnGroup">
            <button onClick={this.increment}>+</button>
            <button onClick={this.decrement}>-</button> 
          </div>
        </div>
      </div>
    );
  }
}
 
export default Counter;
  1. container -> App.js

container 中的 组件被称为 容器组件,通过connect包装UI组件产生新的组件

import React, { Component } from 'react';
import {connect} from 'react-redux';
import {incre,decre} from './../redux/actions'
import Counter from './../components/Counter';
/**
 * 用来将redux管理的state数据映射成UI组件的一般属性的函数
 */
function mapStateToProps(state) {
  return {
    count: state
  }  
}

/**
 * 将包含dispatch代码的函数映射成UI组件的函数属性的函数
 *  
 */
function mapDispatchToProps(dispatch) {
  return {
    incre: (number)=>dispatch(incre(number)),
    decre: (number)=>dispatch(decre(number))
  }
}
/**
 * map 映射的意思
 */
export default connect(
  mapStateToProps, // 指定一般属性
  mapDispatchToProps, // 指定函数属性
)(Counter);

/**
 * 高阶组件:
 *  接收一个组件,返回一个组件函数
 *  connect() 高阶函数,返回的函数是一个高阶组件,接收一个UI组件,生成一个容器组件
 *  容器组件的责任:向UI组件传入特定的属性
 */

上面代码简化版本:

import React, { Component } from 'react';
import {connect} from 'react-redux';
import {incre,decre} from './../redux/actions'
import Counter from './../components/Counter';

// 最终的简化版本(如果看不懂,请看👆)
export default connect(
  state=> ({count:state}),
  {incre,decre}
)(Counter);

  1. 修改 src->index.js
/**
 * 入口JS
 */
import React from 'react';
import ReactDom from 'react-dom';
import App from './container/App';
import store from './redux/store';
import {Provider} from 'react-redux';
// ReactDom.render(<App store={store} />,document.getElementById('root'));

// // 默认情况下 store中的数据发生改变,不会触发组件的更新
// store.subscribe(() => { // store内部的状态数据发生改变时的回调
//   // 重新渲染APP组件标签
//   ReactDom.render(<App store={store} />,document.getElementById('root'));  
// })
ReactDom.render((
  <Provider store={store}>
    <App />
  </Provider>
),document.getElementById('root'));

// Provider 会监听数据的变化,并更新组件

redux-thunk 介绍

用来实现redux异步的中间件插件(默认redux是无法进行异步操作的)

npm install --save redux-thunk

  1. store.js 使用 redux-thunk

    import {createStore,applyMiddleware} from 'redux';
    import reducer from './reducer';
    
    // redux-thunk只有一个函数 thunk
    // 用来实现redux异步的中间件插件
    import thunk from 'redux-thunk';
    
    export default createStore(reducer,applyMiddleware(thunk));
    
  2. action.js 中新增一个异步操作

    import {INCREMENT,DECREMENT} from './action-types';
    
    export const incre = (number)=>({type:INCREMENT,data:number});
    // 返回对象
    export const decre = (number)=>({type:DECREMENT,data:number});
    
    // 增加异步的action,返回的是函数
    export const increAsync = number => {
      return dispatch => {
        /**
         * 执行异步操作(定时器,ajax请求,promise)
        */
        setTimeout(() => {
          // 当异步任务执行完成时,分发一个同步action
          dispatch(incre(number));
        }, 1000);
      }
    }
    
  3. container->App.js 引入 increAsync 异步操作的方法

  4. 在相对应的UI界面中调用

combineReducers 整合多个reducer

  1. reducer.js

    • 导入 import {combineReducers} from 'redux'

      // 根据当前state和action返回一个指定的action
      import {INCREMENT,DECREMENT} from './action-types';
      import {combineReducers} from 'redux'
      
      function count (state=1,action) {
        switch (action.type) {
          case INCREMENT:
            return state + action.data;
          case DECREMENT: 
            return state - action.data;
          default:
            return state;
        }
      }
      
      function user (state ={},action) {
        switch (action.type) {
          default:
            return state;
        }
      }
      
      export default combineReducers({
        count,
        user
      })
      // export default count;
      
  2. 但是此时项目会报错,因为 我们 在 container -> App.js 中代码写错了

    function mapStateToProps(state) {
      return {
        count: state.count // 原本是 count: state
      }
    }