Redux 的使用

131 阅读5分钟

父子组件来回传 而兄弟组件无能为力 因此需要redux全局托管数据

redux 是一种数据管理模式 不仅仅可以用于react 还可以配合其他类库或框架使用 使用redux托管 redux会导出一个store 其中包含获取状态的方法 以及变更状态的方法

  1. 安装 redux
yarn add redux --save
  1. 创建store 需要使用redux 的 createStore方法 使用前需要导入
import {createStore} from 'redux'
  1. create创建store 需要reducer函数 **使用redux不能直接修改状态 需要定义修改状态的函数 这个函数称为reducer **
  2. reducer 函数会接收两个参数
  • state 是redux 托管的数据对象 在创建reducer时给state设置默认值 就是state的初始值
  • action 是修改状态的具体的动作及修改状态需要的参数 是一个带有type字段的对象{type:'ADD',...payload},而reducer的作用就是根据不同的action.type返回一个新的状态对象 使用reducer首先需要返回一个默认状态
function reducer(state = { num: 0 }, action) {
  // state 就是当前 redux 托管的数据对象,在创建 reducer 时给 state 设置的默认值就是 state 的初始值
  // action 是修改状态具体的动作以及修改状态需要的参数,是一个带有 type 字段的对象 { type: 'ADD', ...payload } ,而 reducer 的作用就是根据不同的 action.type 返回一个新的状态对象

  switch (action.type) {
    case 'ADD':
      return {
        num: state.num + action.amount
      }

    case 'MINUS':
      return {
        num: state.num - action.amount
      }
  }

  // 使用 reducer 首先需要返回一个默认状态
  return state
}
let store = createStore(reducer)

export default store
  1. 在组件中导入store
  • store.getState() 初始化state
  • 当修改数据时 需要派发行动 dispath action store.dispatch({type:'ADD',amount:12}), dispatch执行的传递对象 就是action action对象会传递给reducer函数的第二个参数
  • 若需要数据修改更新视图 需要订阅这个数据发生变化后的事件 若想修改视图就更新state 在组件挂载的钩子中的订阅
  • 订阅后会有取消订阅的需要 订阅函数会返回取消订阅的函数 在组件将要销毁的钩子中执行取消订阅的操作
  1. 导入
import store from '../store'
 1. 使用store.getState() 初始化state
constructor(props,context){
    super()
    this.state = {
        num:store.getState().num
    }
}
 2. store.dispatch(actionObj)修改状态
<button onClick = {()=>store.dispatch({type:'ADD',amount:1})} + </button
>
 3. store.subscribe(callback)       store订阅状态更新以后执行的回调函数 以遍更新视图
componentDidMount(){
    <!--订阅状态改变之后做的事情-->
    this.unsub = store.subscribe(()=>{
        this.setState({
            num:store.getState().num
        }))
    })
}
import React, { Component } from 'react'
import ReactDOM from 'react-dom'

import Computed from './Computed'

import store from '../store'

window.__store = store

class Counter extends Component {
  constructor (props, context) {
    super()

    this.state = {
      num: store.getState().num
    }
  }

  componentDidMount () {
    // 订阅状态变化后做的事情
    this.unsub = store.subscribe(() => {
      this.setState({
        num: store.getState().num
      })
    })
  }

  componentWillUnmount () {
    this.unsub()
  }

  render () {
    return (<div className="container">
      <button onClick={() => store.dispatch({type: 'ADD', amount: 1})}>+</button>
      <span>{this.state.num}</span>
      <button onClick={() => store.dispatch({type: 'MINUS', amount: 1})}>-</button>
      <Computed num={this.state.num} />
    </div>)
  }
}

ReactDOM.render(<Counter />, document.getElementById('root'))
  1. 提取action的type
let ADD = 'ADD'
let MINUS = 'MINUS'
  1. reducer合并 reducer是单一状态数 整个应用内所有的数据都要保存到一个store中 所以若有多个组件的状态 就会有多个reducer 而createStore 只能接收一个reducer 因此需要reducer合并
  • redux 中的combineReducers 是用来整合状态的方法 他接收一个对象作为参数 会把多个reducer整合到一个中
  • 整合后会返回一个新的reducer 在创建store时 传入整合后的reducer
  • 状态也会整合
import { createStore, combineReducers } from 'redux'

// 把 action 的 type 提取出来,作为宏
let ADD = 'ADD'
let MINUS = 'MINUS'

// 使用 redux 我们不能直接修改状态,我们需要定义修改状态的函数,这个函数称为 reducer
// 一个用来管理状态的函数
function counter(state = { num: 0 }, action) {
  // state 就是当前 redux 托管的数据对象,在创建reducer时给 state 设置的默认值就是 state 的初始值
  // action 是修改状态具体的动作以及修改状态需要的参数,是一个带有 type 字段的对象 { type: 'ADD', ...payload } ,而 reducer 的作用就是根据不同的 action.type 返回一个新的状态对象

  switch (action.type) {
    case ADD:
      return {
        num: state.num + action.amount
      }

    case MINUS:
      return {
        num: state.num - action.amount
      }
  }

  // 使用 reducer 首先需要返回一个默认状态
  return state
}

let todoState = {
    list: [],
    filter: 'all'
}


function todo(state = todoState, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return {
                ...state,
                list: [ // 这个 list 会覆盖上面 ... 出来的 list
                    ...state.list,
                    action.content
                ]
            }
  }
  return state
}

// combineReducers 是用来整合状态的方法,它接收一个对象作为参数,会把多个 reducer 整合到一个中
// 整合后的 返回一个新的 reducer ,同时 状态也被整合,例如上面的 counter 和 todo 整合后的状态对象: { todo: {list: [], filter}, counter: {num: 0}}

let combinedReducer = combineReducers({
  todo: todo,
  counter: counter
})

let store = createStore(combinedReducer)

export default store
  1. 使用整合后的store 整合后的store带有命名空间 在组件中使用时需要通过对应的命名空间获取状态
  2. actionCreator 动作创建器 写一个函数专门生成dispatch需要的action对象 这个函数称为action-creator 这样在dispatch时 只需调用action-creator 并且传入payload即可
import * as Types from '../action-type'

function add(amount) {
  return {
    type: Types.ADD,
    amount
  }
}

function minus(amount) {
  return {
    type: Types.MINUS,
    amount
  }
}

// add 和 minus 就是 action-creator,而 1 是传递给 上面的 amount,即 payload
<button onClick={() => store.dispatch(add(1))}>+</button>
<span>{this.state.num}</span>
<button onClick={() => store.dispatch(minus(1))}>-</button>
  1. 文件拆分 react的模块化特性很强 对应着redux的使用也需要差分成模块化达到代码解耦的目的

  • index.js 负责组合combineReducers 和导出store
  • action 存放的actionCreate
  • action-type 定义组件的action需要的type
  • reducer 存放reducer
/store/index.js
import {createStore, combineReducers} from 'redux'

import { counter } from './reducer/counter'
import { todo } from './reducer/todo'

let combinedReducer = combineReducers({
  todo: todo,
  counter: counter
})

let store = createStore(combinedReducer)

export default store

store/action/counter.js
import * as Types from '../action-type'

function add(amount) {
  return {
    type: Types.ADD,
    amount
  }
}

function minus(amount) {
  return {
    type: Types.MINUS,
    amount
  }
}

export { add, minus }
----------------------------------------------------------------
store/action/todo.js

import * as Types from '../action-type'

function addTodo(content) {
  return {
    type: Types.ADD_TODO,
    content
  }
}

export { addTodo }
-------------------------------------------------
/store/action-types/index.js
export const ADD = 'ADD'
export const MINUS = 'MINUS'

export const ADD_TODO = 'ADD_TODO'
---------------------------------------------------
/store/reducer/counter
import * as Types from '../action-type'
export function counter(state = {num: 0}, action) {
  switch (action.type) {
    case Types.ADD:
      return {
        num: state.num + action.amount
      }

    case Types.MINUS:
      return {
        num: state.num - action.amount
      }
  }

  // 使用 reducer 首先需要返回一个默认状态
  return state
}
---------------------------------------------
/store/reducer/todo.js

import * as Types from '../action-type'

let todoState = {
  list: [],
  filter: 'all'
}


export function todo(state = todoState, action) {
  switch (action.type) {
    case Types.ADD_TODO:
      return {
        ...state,
        list: [ // 这个 list 会覆盖上面 ... 出来的 list
          ...state.list,
          action.content
        ]

      }
  }
  return state
}