简单聊下 React 中状态存储库 Redux
介绍
传统的组件通信有很多,如父子组件通过 props 通信,pubsub-js 通信等,但都有或多或少的限制,如只能实现父子组件通信,需要不停的订阅发布...,导致难以维护,Redux 就解决了以上问题。
不太了解的小伙伴可简单先看下 官网
本篇主要解决哪些问题:
- 如何在工程化项目中引入 Redux
- 如何使用异步 action
- 如何使用多个 reducer
好了,开始做些准备工作,如果您不想往下看代码,也可直接去看 react_extends 项目下 at_redux 分支 查看。
准备
- 简单了解 Redux 是干什么的,以及怎么使用
- 准备一个 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
- 首先安装 redux 依赖,注意不是 Facebook 团队出品的 react-redux
yarn add redux
- 引入 redux-thunk 依赖,作为异步中间件
yarn add redux-thunk
- 以计数器为例,新建 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>
)
}
}
运行结果如下:
计数器逻辑
实现目标:
- 加一
- 减一
- 异步加
对 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>
)
}
}
收尾
好了,到了这里工作就差不多结束了,只差把 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>
)
}
最后
来张录屏