③ Redux

Redux

react 和 redux 实际上是两个独立的产品,项目中可以使用 redux,也可以使用 redux 而不使用 react

  • redux 是一种新型的前端架构模式

1 Redux 设计和使用的三个原则

  1. store 必须是唯一的
  2. 只有 store 能改变自己的内容

reducer 可以接受 state,但是绝对不能修改 state

  1. Reducer 必须是一个纯函数

纯函数:不依赖执行上下文,也不影响上下文的变量,输出只由输入决定

2 基本概念

2.1 核心对象 Store

storeredux 存储区 (一个保存数据的地方),规定一个应用只能有一个 storestore 中的数据可以被直接访问,但只能通过提供的 reducer 进行更新

1. 生成数据仓库的方法

  • createStore(reducer)

2. 常用方法

  • store.getState() -- 获取仓库最新 state 状态(数据)
  • store.dispatch(action) -- 修改 state 的唯一方法
  • store.subscribe(fn) -- 监听数据修改
componentDidMount() {
  // 获取store数据并监听修改(手动)
  let cartLen = store.getState().goodslist.length;
  store.subscribe(() => {
    cartLen = store.getState().goodslist.length;
    this.setState({
      cartLen
    })
  })
  this.setState({
    cartLen,
    currentPath: this.props.location.pathname
  })
}

2.2 数据存储 State

state 为数据状态(快照,即数据在某个时间点的状态),State 改变则 View 改变

  • store.getState()

2.3 状态更新提交 Dispatch

  • store.dispatch(action)

2.4 状态更新参数 Action

Action 用于定义如何改变 state,是用户改变 state 的唯一方式

1. 格式:{ type: 'UPDATE_CART', payload }

  • type:一个简单的字符串常量
  • payload:用于更新状态的数据
store.dispatch({ type: 'add_to_cart', payload: goods })

2. Action Creator

每次编写 action 对象比较麻烦,可以封装一个函数用于生成 action

export function updateCart(todo) {
  return {
    type:'UPDATE_CART',
    payload:todo
  }
}

3. bindActionCreators

利用 redux 的 bindActionCreators 把 ActionCreator 中的所有方法(export default 中的方法)绑定到组件 props 并自动隐式调用 dispatch(action)

import { bindActionCreators } from redux;
import { connect } from 'react-redux';
import ActionCreator from 'actions';

//...

MyComponent = connect(
  state => state,
  dispatch => bindActionCreators(ActionCreator, dispatch)
)(MyComponent)

2.5 状态更新逻辑 Reducer(修改数据的方式)

reducer 必须是一个纯函数,用于指定 state 修改逻辑,它接收当前 state 和 action 作为参数,并根据 state 和 action 的值返回新的 state

//设置默认值
let defaultState = { goodslist: [], step: 0 }
let cartReducer = (state = defaultState, action) => {
  switch(action.type) {
    case 'UPDATE_CART':
      return { ...state, goodslist: action.payload }
    default:
      return state;
  }
}

1. 处理多个 Reducer:combineReducers

只对 state 有影响,对 patch 无影响

import { createStore, combineReducers } from 'redux'
import productsReducer from './productsReducer'
import cartReducer from './cartReducer'

// 合并 Reducer
const allReducers = {
  products: productsReducer,
  shoppingCart: cartReducer
}
const rootReducer = combineReducers(allReducers)
let store = createStore(rootReducer)

3 rudex 使用步骤

  1. 创建 store(需要指定 state 与 state 的修改方式)
    • 需要定义 state
    • 需要定义 reducer
  2. 定义 state:状态(数据)
  3. 定义 reducer
    • 修改 state 的方式
    • 必须返回一个新的 state
  4. 使用
    • 获取:store.getState()
    • 修改:store.dispatch(action)

4 react-redux

利用 context 把 store 上的数据传入到 react 组件中

  • react-redux 提供了一些接口,用于 redux 的状态和 react 的组件展示结合起来,以实现状态和视图的一一对应

4.1 使用步骤

1. 在最外层使用 <Provider/> 组件,并共享 Store

  • 组件 <Provider>

react-redux 提供 provider 组件,接收 reduxstore 作为 props,并将其声明为 context 的属性之一
可结合 connect 方法可以让容器组件方便获取 statedispatch

ReactDOM.render(
  <Provider store={store}>
    <HashRouter>
      {/* <Route component={App}/> */}
      <App/>
    </HashRouter>
  </Provider>
  , document.querySelector('#app')
)

2. 使用 conect 高阶组件映射数据到组件的 props

  • 高阶组件 connect()

connect 方法为 react-redux 核心方法,它把 react 组件与 redux 中的 store 真正连接在一起,connect 方法接收两个参数(function 类型),用于定义了ui组件的业务逻辑,返回容器组件

  • 格式:connect([mapStateToProps], [mapDispatchToProps])(Component) 函数柯里化
mapStateToProps

将 state 中的数据映射到 props

  • 当 state 更新时,会自动重新渲染
  • 必须返回一个对象
const mapStateToProps = function(state) {
  //映射state数据到App组件的props
  console.log('state:', state);
  return {
    cartLen: state.goodslist.length
  }
}
mapDispatchToProps

将操作映射到 props,映射成 action

const mapDispatchToProps = dispatch => {
  return {
    clear() {
      dispatch({ type: 'clear_cart' })
    },
    changeQty(goods_id, goods_qty) {
      dispatch({ type: 'change_goods_qty', payload: { goods_id, goods_qty }})
    },
    remove(goods_id) {
      dispatch({ type: 'remove_from_cart', payload: { goods_id }})
    }
  }
}	

5 生成器 & 迭代器

5.1 如何处理ajax请求

ES6新特性

5.2 Generator 生成器

  • return:结束并返回值
  • yield:暂停并返回值

5.3 Iterator 迭代器

  • for...of:能遍历具有迭代器特性的数据
  • next():返回数据格式为 { value, done }

5.4 生成器函数返回一个迭代器

  • function* name(){}

执行生成器函数的代码要使用 next() 方法
yield 暂停之后用 next() 方法继续执行

function* show() {
  console.log(1)
  yield 'zhangsan'
  console.log(2)
  yield 18
  console.log(3)
  return 100
  console.log(4)
}
  • for...of 默认不能遍历对象,但给对象添加迭代器就可以遍历

添加 Symbol.iteratornext 属性

let zhangsan = {
  age: 18
}
Object.defineProperties(zhangsan, {
  [Symbol.iterator]: {
    value() {
      var idx = 0;
      return {
        next: () => {
          var done = idx >= Object.keys(this.length)
          var key = Object.keys(this)[idx]
          var value = this[key]
          idx++
          return {
            value: [key, value],
            done
          }
        }
      }
    }
  }
})

6 redux-saga中间件

引入 saga 即为函数

6.1 saga 的使用步骤

  1. 创建 saga 中间件
  2. 将该中间件连接至 store

vue 的 action -> redux 只提供接口 applyMiddleWare

  • createStore 方法中只能有一个中间件
    1. 第一种:compose 组合中间件
    2. 第二种:调试工具内部已有 compose
  1. 运行 saga 配置

redux-saga 内部会自动调用 next(),(等结果返回后调用)

  • 如何编写saga配置文件?

6.2 使用 saga 需要考虑两个问题

  1. 如何监视用户操作?(如:购物车数量修改)
  2. 如何等待异步操作的结果返回,并触发reducer修改?-- yiled
  • 监听
    • takenEvery -- 可同时触发多次
    • takeLatest -- 仅允许触发最后一次
posted on 2021-09-24 11:24  pleaseAnswer  阅读(21)  评论(0编辑  收藏  举报