简单聊下 React 中状态存储库 Redux

介绍

传统的组件通信有很多,如父子组件通过 props 通信,pubsub-js 通信等,但都有或多或少的限制,如只能实现父子组件通信,需要不停的订阅发布...,导致难以维护,Redux 就解决了以上问题。
不太了解的小伙伴可简单先看下 官网

本篇主要解决哪些问题:

  1. 如何在工程化项目中引入 Redux
  2. 如何使用异步 action
  3. 如何使用多个 reducer

好了,开始做些准备工作,如果您不想往下看代码,也可直接去看 react_extends 项目下 at_redux 分支 查看。

准备

  1. 简单了解 Redux 是干什么的,以及怎么使用
  2. 准备一个 React 工程化项目

at_redux 项目

新建一个 at_redux 项目,删除多余的文件,只保留以下目录,注意 App.js 改为了 App.jsx 文件:

- public
  - index.html
- src
  - App.jsx
  - index.js

此时的几个文件内容如下:

// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

ReactDOM.render(<App />, document.getElementById('root'))

// App.jsx
import React, { Component } from 'react'

export default class App extends Component {
  render() {
    return <div>App</div>
  }
}

// index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>at_redux</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

运行结果:

结果图片

引入 redux

  1. 首先安装 redux 依赖,注意不是 Facebook 团队出品的 react-redux
yarn add redux
  1. 引入 redux-thunk 依赖,作为异步中间件
yarn add redux-thunk
  1. 以计数器为例,新建 redux 目录,此时 src 目录如下
- src
  - components
    - Count
      - index.jsx
  - redux
    - actions // 存储 action 的集合
      - count.js
    - reducers // 存储 reducer 的集合
      - count.js
    - constant.js // 变量的维护集合,消除魔术字符串
    - index.js // redux 的入口文件
  - App.jsx
  - index.js

此时变动的文件如下:

// components/Count/index.jsx
import React, { Component } from 'react'

export default class Count extends Component {
  render() {
    return <div>count</div>
  }
}

// App.jsx
import React, { Component } from 'react'
import Count from './components/Count'

export default class App extends Component {
  render() {
    return (
      <div>
        <Count />
      </div>
    )
  }
}

运行结果如下:

运行结果

计数器逻辑

实现目标:

  1. 加一
  2. 减一
  3. 异步加

对 Count/index.jsx 简单改造,此时简单逻辑如下:

点击查看代码
import React, { Component } from 'react'

export default class Count extends Component {
  addCount = () => {
    console.log('加一')
  }

  reduceCount = () => {
    console.log('减一')
  }

  asycCount = () => {
    console.log('异步加一')
  }

  render() {
    return (
      <div>
        <button onClick={this.addCount}>加一</button>
        <button onClick={this.reduceCount}>减一</button>
        <button onClick={this.asycCount}>异步加一</button>
        <div>count: {0}</div>
      </div>
    )
  }
}
稍微加了点样式,此时运行结果如下:

结果

点击对应项,也会发现控制台都出现了对应内容:

控制台

此时开始对 redux 开始进行改造,对应的文件内容如下:
改动的地方有点多,如果您还不熟悉,请耐心查看,会在适时的地方有注释

点击查看代码
// redux/constant.js
export const ADD_COUNT = 'ADD_COUNT'
export const REDUCE_COUNT = 'REDUCE_COUNT'
export const ASYC_COUNT = 'ASYC_COUNT'

// redux/reducers/count.js
import { ADD_COUNT, REDUCE_COUNT } from '../constant'

export default function (preState = 0, action) { // 注意:这里设置了默认值,相当于初始值,用于什么操作都没有的时候展示
  const { type, data } = action
  switch (type) {
    case ADD_COUNT:
      return preState + data
    case REDUCE_COUNT:
      return preState - data
    default:
      return preState // 注意,这里的 default 不能省
  }
}

// redux/reducer/index.js
import { combineReducers } from 'redux'
import count from './count'

// 注意:多个 reducer 合并,这里一定要加上 combineReducers,否则会报错误
export default combineReducers({
  count
})

// redux/actions/count.js
import store from '../index'
import { ADD_COUNT, REDUCE_COUNT, ASYC_COUNT } from '../constant'

export const actionAddCount = (data) => ({ type: ADD_COUNT, data })
export const actionReduceCount = (data) => ({ type: REDUCE_COUNT, data })
export const actionAsycCount = (data) => {
  // 使用 setTimeout 模拟异步
  // 注意:异步加本身也是一个加法的操作,所以此时我们调用的还是 actionAddCount 这个操作,所以这里使用了 distatch 触发
  return setTimeout(() => {
    store.dispatch({ type: ADD_COUNT, data })
  }, 200)
}

// redux/index.js
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk' // 处理异步的中间件
import reducers from './reducers/index' // 注意:这里的 reducers 文件夹下加了一个 index.js,引入所有的 reducer

export default createStore(reducers, applyMiddleware(thunk)) // applyMiddleware 引入 thunk 插件

// src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/index'

ReactDOM.render(<App />, document.getElementById('root'))

// 当 redux 中状态变化时更新页面
store.subscribe(() => {
  ReactDOM.render(<App />, document.getElementById('root'))
})

// components/Count/index.jsx
import React, { Component } from 'react'
import store from '../../redux/index'
import {
  actionAddCount,
  actionReduceCount,
  actionAsycCount
} from '../../redux/actions/count'
import './index.css'

export default class Count extends Component {
  addCount = () => {
    console.log('加一')
    store.dispatch(actionAddCount(1))
  }

  reduceCount = () => {
    console.log('减一')
    store.dispatch(actionReduceCount(1))
  }

  asycCount = () => {
    console.log('异步加一')
    store.dispatch(actionAsycCount(1))
  }

  render() {
    console.log('store', store.getState()) // 可查看打印
    return (
      <div className="count">
        <button onClick={this.addCount}>加一</button>
        <button onClick={this.reduceCount}>减一</button>
        <button onClick={this.asycCount}>异步加一</button>
        <div className="count-con">count: {0}</div>
      </div>
    )
  }
}
如果您此时查看控制台,会发现得到的 store 是一个对象,这就是多个 reducer 返回的对象,如果是没有采用多个 reducer 的方式,这里将直接返回对应状态的结果,而不是这种 object 包裹的 key-value 形式

结果

收尾

好了,到了这里工作就差不多结束了,只差把 reducer 里的状态动态的显示在页面上就好了,所以需要对 components/Count/index.jsx 简单改造下,内容非常简单:

render(){
  const { count } = store.getState() // 只变动了这里,是不是很 easy,其他 reducer 状态下的变量也是通过这种方式引入
  return (
    <div className="count">
      <button onClick={this.addCount}>加一</button>
      <button onClick={this.reduceCount}>减一</button>
      <button onClick={this.asycCount}>异步加一</button>
      <div className="count-con">count: {count}</div>
    </div>
  )
}

最后

来张录屏
完整功能

posted @ 2021-12-18 13:05  土地情缘  阅读(151)  评论(0编辑  收藏  举报