react笔记_11 redux

redux

redux中文文档

定义

Redux 是 JavaScript 状态容器,提供可预测化的状态管理 --> 是一个专门做状态管理的js库。
redux并不是react的插件库 --> Redux 除了和 React 一起用外,还支持其它界面库(如angular, vue 等)。 它体小精悍(只有2KB–包括依赖)。

使用时机

  • 不需要使用时
    • 如果你不知道是否需要 Redux,那就是不需要它

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

    • 简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。
  • 需要使用时
    • 若是组件间状态比较复杂,可以使用redux.
      • 比如:某个组件的状态,需要共享
      • 比如:某个状态需要在任何地方都可以拿到
      • 比如: 一个组件需要改变全局状态
      • 比如:一个组件需要改变另一个组件的状态

redux基本概念

Store

Redux为单一数据源。

整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

将state、action、reducer 关联在一起;

State

在 Redux API 中,State指一个唯一的 state 值

  • state 就是Redux 应用的全部状态 即 Redux管理的全部数据,通常为一个多层嵌套的对象。
  • state 由 store 管理
  • 当想要获取state数据时 通过API -> getState 获取。
Action

Action 是一个普通对象,用来表示即将改变 state 的意图

State 是只读的,唯一改变 state 的方法就是触发 action。它是将数据放入 store 的唯一途径。

约定俗成,action 必须拥有一个 type属性,表示动作类型,其他属性由自己决定。

reducer

Reducer 的作用是用于描述action 如何改变 state tree。

Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state

可用于初始化状态、加工状态;

redux工作原理

在这里插入图片描述
从原理图来看 compoents 、Action Creators、 Reducers、Stroe -> 表示组件、action 、reducer都是存在多个,而Stroe是唯一的。

若是仅存在一个组件,根本不需要使用redux,只需要使用组件内部状态管理就可以实现功能。
所以现在假设存在多个组件,组件的数据使用 redux 统一管理。当某个组件想要修改数据时

  • [1] 组件想要修改数据 -> 告诉 Action创建者
  • [2] Action 创建者去创建一个Action动作对象(里面包含干什么、如何干)
  • [3] 通过 dispatch函数将 Action动作对象 派发出去
  • [4] Store(调度者) 接收到action,将action和preState(原来的值)交给 reducer
  • [5] reducer 进行计算 并将计算得到的值 交给 Store
  • [6] 组件想要获取数据时 再通过 getState方法 获取更新后的值

举一个最简单的例子

  • component:客人
  • Action Creators : 服务员
  • dispatch: 点菜宝
  • Store: 老板
  • reducer:厨师

语法

[1] 创建Store
createStore
combineReducers

Store 是通过 createStore 方法创建的

  • (1) 引入createStore
    import { createStore } from 'redux'
    
  • (2) 创建Store
    createStore( reducer )
    
    • 若是仅仅存在一个reducer,我们可以直接引入(值为一个函数)
      import { createStore } from 'redux'
      import numReducer from './numReducer'
      const store =  createStore( numReducer )
      
      此时若是获取state, 得到的值就是 该reducer返回的值,比如numReducer返回值为0,此时通过getState得到的值就是0。
    • 若是存在多个reducer, 就需要通过 combineReducers方法进行reducer合并
      import { createStore, combineReducers } from 'redux'
      import numReduce from './numReduce'
      import countReduce from './countReduce'
      const reduces = {
        num:numReduce,
        count:countReduce
      }
      const store =  createStore( combineReducers(reduces) )
      
      此时若是获取state, 得到的值就是 一个对象
      {
        num:numReduce函数的返回值,
        count:countReduce函数的返回值
      }
      
[2]创建并分发action
dispatch
applyMiddleware

使用Action Creator 去创建Action,使用dispatch去派发action。

语法

action的值可以是一个对象,也可以是一个函数

  • 若是值为一个对象 表示是同步更新数据
      {
        type: 动作指令(String类型)data:  操作数据
      }
    
  • 若是值为一个函数 表示是异步更新数据
    // 该方法默认会将dispatch作为函数传入
    (dispatch)=>{
      setTimeOut(()=>{
        dispatch({type:动作指令,data:  操作数据})
      },0) // 使用定时器模拟异步操作
    }
    
举例说明- 做一个加法运算
  • 同步加法
    • action
      export const addAction = data => ({type:INCREMENT,data})
      
    • 在组件中派发此action
      import {addAction} from '../../../store/action.js
      import store from '../../../store'
      // 派发
      store.dispatch(addAction( value ))'
      
  • 异步加法
    • action
      export const addActionAsync = (data, time) =>{
        return (dispatch)=>{
          setTimeout(()=>{
            dispatch({type:'add',data})
          },time)
        }
      }
      
    • 在组件中派发action
       import {addActionAsync} from '../../../store/action.js
       import store from '../../../store'
       // 派发
       store.dispatch(addActionAsync( value ))'
      
执行原理

当使用dispatch去派发action时,在Store会接收到-> Stroe会去查看action的类型

  • 若是类型为一个对象 -> Store会将action交给 reducer

  • 若是类型为一个函数 -> 会报错
    在这里插入图片描述
    希望得到的值为一个对象。
    若是希望action能够兼容值为函数的情况(也就是能够使用异步Action),需要使用一个中间件 redux-thunk

  • 使用redux-thunk中间件后,若是类型为一个函数 -> store会调用该函数并将dispatch 方法作为参数传入(而不会交给reducer)

redux-thunk中间件使用
  • 作用:可以让sotre识别 函数类型的action
  • 使用
    • [1] 下载
      使用命令npm i redux-thunk 下载
    • [2] 配置
      因为我们的目的是想让Stroe能够识别 函数类型的action,所以在store创建过程进行配置 -> 使用applyMiddleware配置中间件
      import { createStore, applyMiddleware } from 'redux'
      const store = createStore(
      // 合并
      combineReducers(reducers),
      // 中间件
      applyMiddleware(Thunk)
      )
      
[3]创建reducer
语法
const init = 初始化的值 // 若是不设置,第一次渲染时默认为undefined
export default function( preState=init, action ){
  const {type, data} = action
  switch (type){
    case type值匹配:
      // 匹配后的逻辑处理
      return 新的state值
    ...
    default:
      return preState
  }
}
渲染过程
  • 在第一次进入页面时 会默认调用所有的reducer, 此时传入的 preState值为
    undefined
    
    默认传入的action值为
    {
      type:@@init... // 后面拼接随机字符
    }
    
  • 后面每次通过dispatch派发action时都会调用对应的reducer
[4]getState

作用:用于获取数据
语法

import { createStore } from 'redux'
const store = createStore(reducer)

store.getState() // 获取数据
[5]subscribe

作用:用于监听redux中状态的变化,只要变化,就会走subscribe函数;
原因: redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己来;
语法

import { createStore } from 'redux'
const store = createStore(reducer)

store.subscribe(()=>{
  console.log('逻辑代码-> 更新数据')
})
举例说明

下面示例的编写的前提是了解redux的API的基础之上,若是没有了解过,可以结合下方的 API一起看;
在这里插入图片描述

现在有如上示例

  • 第一个:num的值;
  • 第二个:操作数,可以选择为1、2、3
  • 第三个:按钮,点击num的值变为 当前值+操作数
  • 第四个:按钮,点击num的值变为 当前值-操作数
  • 第五个:按钮,点击若是当前num的值为偶数->无变化;若是当前num的值为奇数-> num的值变为 当前值+操作数;
  • 第六个:按钮,点击异步增加num的值

若是值存储在当前组件的state中,代码如下

import React, { Component } from 'react'
export default class New extends Component {
 state = { num: 0 }
 render() {
   const { num } = this.state
   return (
     <div>
       {num}
       &nbsp;
       &nbsp;
       <select ref={this.getref}>
         <option value='1'>1</option>
         <option value='2'>2</option>
         <option value='3'>3</option>
       </select>
       &nbsp;
       <button onClick={()=>{this.calculate('increment')}}>+</button>&nbsp;
       <button onClick={()=>{this.calculate('decrement')}}>-</button>&nbsp;
       <button onClick={()=>{this.calculate('incrementIfOdd')}}>和为基数再加</button>&nbsp;
       <button onClick={()=>{this.calculate('incrementAsync')}}>异步加</button>&nbsp;
     </div>
   )
 }
 getref = (dom) => {
   this.select = dom
 }
 calculate = (type)=>{
   // 获取到select选择的值
   const { value } = this.select
   const { num } = this.state
   switch (type){
     case 'increment':
       this.setState({num: num + +value})
       break;
     case 'decrement':
       this.setState({num: num - +value})
       break;
     case "incrementIfOdd":
       if(num % 2 != 0){
         this.setState({num: num + +value})
       }
       break;
     case 'incrementAsync':
       setTimeout(()=>{
         this.setState({num: num + +value})
       }, 100)
   }
 }
}
配置redux
[1] 下载

使用命令npm install --save redux下载redux

[2] 配置
  • (1)在src文件夹下创建一个store文件夹
  • (2) 在store文件夹下创建一个index.js文件 -> 创建store并导出
    import { createStore ,combineReducers,applyMiddleware} from 'redux'
    import reducers from './reduces'
    import Thunk from 'redux-thunk'
    export default createStore(
      combineReducers(reducers), // 合并reducer
      applyMiddleware(Thunk) // 配置中间件
    )
    
  • (3) 在store 文件夹下创建一个reducers文件夹(因为reducer可以有多个),有多少个reducer就在reducers文件夹下创建多少个文件
    • num的reducer -> num.js
      // num默认值为0
      import {INCREMENT, DECREMENT} from '../static'
      export default function( preState=0, action ){
        const {type, data} = action
        switch (type){
          case INCREMENT:
            return preState + data
          case DECREMENT:
            return preState - data
          default:
            return preState // 初始化的时候type为 @@init...
        }
      }
      
    • 集成到reduces文件夹下的index.js文件中
      // 将所有定义的数据发导入到这个对象中然后返回
      
      import numReduce from './num'
      
      const reducers = {
        num: numReduce // 这个值将会作为创建的state中数据的属性名‘num’
      };
      
      export default reducers;
      
  • (4)在store 文件夹下创建一个actions文件夹(因为action可以有多个),有多少组action就在actions文件夹下创建多少个文件
    • 有关num的action -> num.js
      // action可以是一个对象 也可以是一个函数
      // 若是action 为一个对象表示同步更新数据,action为一个函数表示异步更新数据
      import {INCREMENT, DECREMENT} from '../static'
      export const addAction = data => ({type:INCREMENT,data})
      export const decreaseAction = data => ({type:DECREMENT,data})
      export const addActionAsync = (data, time) =>{
        return (dispatch)=>{
          setTimeout(()=>{
            dispatch(addAction(data))
          },time)
        }
      }
      
    • 集成action到actions文件夹下的index.js中
      import { addAction, decreaseAction, addActionAsync } from './numAction'
      
      export {
        addAction,
        decreaseAction,
        addActionAsync
      }
      
  • (5)创建static.js文件 -> 用于保存action的动作指令
     // 指令
     export const INCREMENT = 'increment' // 加法运算
     export const DECREMENT = 'decrement' // 减法运算
    

react-redux

由于很多开发者都喜欢在react中使用redux, Facebook就开发了 react-redux。
react-redux将组件分为 容器组件UI组件

  • UI组件只负责页面的呈现,交互等;在UI组件中不能使用任何关于Redux的API;
  • 容器组件是真正与Redux打交道的,里面可以随意使用Redux的API,最终将结果交给UI组件;
  • 所有的UI组件都应该包裹一个容器组件,他们是父子关系;
  • 容器组件是连接 UI组件 与 Redux的桥梁,容器组件会传递给UI 组件
    • Redux中所保留的状态;
    • 用于操作Redux的方法;
      在这里插入图片描述
语法
connect

connect用于创建一个容器组件

import {connect} from 'react-redux';
export default connect(function1,function2)(UI组件)
  • connect为一个函数

      connect( 
        state=> {
          return 对象 // return的对象的key ,value将添加在UI组件的props中
        }, 
        dispatch =>{
          return 对象 // return的对象的key ,value将添加在UI组件的props中
        }
      )
    

    参数1(mapStateToProps): 是一个回调函数

    • 该函数是 react-redux 调用,该函数在调用时会将state作为参数传入
    • 函数的返回值为一个对象 ,作为UI组件接收的props- 状态

    参数2(mapDispatchToProps): 是一个回调函数

    • 该函数是 react-redux 调用,在调用时会将dispatch 方法作为参数传入
    • 函数的返回值为一个对象,作为UI组件接收的props- 操作状态的方法

    返回值:connect函数的返回值也是一个函数

    • 调用时将 UI组件 作为参数传入
    • 最终得到的值是一个容器组件

注意点:在使用容器组件时需要将 store作为prop传入

  <Count  stroe={ store }/>

在UI组件中接收-> 在props中除了接收 connect中接收的两个方法的返回值还有默认的store的属性和方法

Provider组件

若是我们有多个组件 ,每次都需要在容器组件传递 store,而且若是组件不是普通组件而是路由组件还不好传递,此时就可以使用Provider组件

// [1] 引入组件
import { Provider } from 'react-redux'
import store from './store'
// [2] 包裹跟组件
<Provider store={store}>
   <App/>
</Provider>

Provide组件会去查看项目里哪些组件为容器组件 将 store 传递

监听

redux是 redux只负责管理状态,至于状态的改变驱动着页面的展示是需要我们自己控制的

若是使用了react-redux,就不需要自己监听了,当状态改变时会自动渲染对应的UI组件

配置
[1]下载

使用命令 npm i react-redux 下载react-redux;

[2]配置

对于上面例子的改进

import React, { Component } from 'react'
import {addAction, decreaseAction, addActionAsync} from '../../../store/actions/index'
import {connect} from 'react-redux'
class New extends Component {
  render() {
    const { numReduce } = this.props
    return (
      <div>
        {numReduce}
        &nbsp;
        &nbsp;
        <select ref={this.getref}>
          <option value='1'>1</option>
          <option value='2'>2</option>
          <option value='3'>3</option>
        </select>
        &nbsp;
        <button onClick={()=>{this.calculate('increment')}}>+</button>&nbsp;
        <button onClick={()=>{this.calculate('decrement')}}>-</button>&nbsp;
        <button onClick={()=>{this.calculate('incrementIfOdd')}}>和为基数再加</button>&nbsp;
        <button onClick={()=>{this.calculate('incrementAsync')}}>异步加</button>&nbsp;
      </div>
    )
  }
  getref = (dom) => {
    this.select = dom
  }
  calculate = (type)=>{
    // 获取到select选择的值
    const { value } = this.select
    const { numReduce,  add, decrease, addAsync} = this.props
    switch (type){
      case 'increment':
        add( +value )
        break;
      case 'decrement':
        decrease( +value )
        break;
      case "incrementIfOdd":
        if(numReduce % 2 != 0){
          add( +value )
        }
        break;
      case 'incrementAsync':
        addAsync( +value , 2000 )
    }
  }
}

export default connect(
  state => {
    return state
  },
  dispatch => {
    return {
      add(value){
        dispatch(addAction( value ))
      },
      decrease(value){
        dispatch(decreaseAction(value))
      },
      addAsync(value,time){
        dispatch(addActionAsync(+value, time))
      }
    }
  }
)(New)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值