前言
react 用了一年多,但一直对 redux 了解不多,一直想找个机会彻底掌握它,所以有了今天这篇文章,如果你也对 redux 不够了解,那么可以仔细阅读下,相信会有所收获。
初识 redux
首先让我们清晰下 什么是redux?它有什么作用?
- redux 是一个独立的、专门用于状态管理的JS库
- 可以搭配 react、angular、vue 使用,不过主要用于搭配 react
- 作用:集中管理 react 应用中多个组件共享的状态。
redux 原理
用文字来描述:
redux 将整个应用的 state 储存在 store 中,视图组件在 redux 中派发 action 方法,
action 通过 store 的 dispatch 方法派发给 store,
store 接收 action,和之前的 state,一起传递给 reducer,
reducer 实现更新 state 的动作后,返回新的 state 给 store,然后 store 去改变自己的 state 。
视图组件连接了 store,store 中的 state 更新后,视图组件的数据也就随之改变,从而重新渲染页面。
三大原则(重点)
1. 单一数据源
整个应用的 state 被储存在一个 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
简单点来说就是:
一个应用只能有一个 store 。
当我们的项目越来越复杂时,我们需要拆分数据处理逻辑,可以使用reducer组合,而不是创建多个store 对象
2. State 是只读的
唯一改变 state 的方法是 触发(dispatch) action,action 是一个对象,描述发生了什么事情,比如点击了按钮之类的操作。
3. 使用纯函数(reducer)执行修改
为了描述 action 如何改变 state 树,我们需要编写 reducers
接下来,我们来来了解怎么创建 store,及其对应的 API,什么是 reducer
store
store 是 redux 的核心,负责整合 action 和 reducer。
redux 提供了 createStore()
方法来创建一个store
import { createStore } from 'redux'
import reducer from './xxx.ts'
const store = createStore(reducer) // createrStore 接收一个 reducer 为参数
createStore
接收一个 reducer 为参数,它其实还有第二个参数,是可选的,用于设置 state 的初始状态
关于 store
,我们常使用的 API 有三个:dispatch
、subscribe
、getState
dispatch:用于更新state。
用法:store.dispatch(action)
注意:action 必须是一个对象,且 action.type 不能省略。
getState:用于获取 state
getState 方法比较简单,就是获取 state,不过在返回 state 之前,会进行 isDispatching 判断,如果 isDispatching 为 true,则表示是在进行 reducer 阶段,等返回最新的 state 之后,将 isDispatching 赋值为 false。
在 reducer 进行中,不能通过使用 store.getState()
来获取 state,因为我们已经通过 (state, action) => {} 传入了 state,不用在通过 getState 方法来获取。如果在 reducer 中使用了 getState,将会抛出一个错误提示。
subscribe:用来监听 state 的变化
一旦 state 发生变化,就会自动执行这个函数。 其实是每次 dispatch 后,紧跟着都会执行我们之前通过 subscribe 注册的监听事件,我们可以在 listener 函数中通过 store.getState 获取最新的 state 状态树。
subscribe 会返回一个函数,调用这个函数就可以解除监听。
useEffect(() => {
// 监听 state 的变化
let unSubscribe = store.subscribe(() => {
console.log('我正在监听', store.getState())
})
return () => {
// 取消监听
unSubscribe()
}
}, [])
action
首先,我们先解释下 action。
action 是把数据从应用传到 store 的有效载荷,它是 store 数据的 唯一来源。
我们通过 store.dispatch()
将action 传到 store。
action 本质上是一个 JavaScript 普通对象,描述已发生的事件,作用是告知 reducer 该更新 store 中哪些 state
action 内必须使用一个字符串类型的 type
字段来表示将要执行的动作。
大部分情况下,type 会被定义为 字符串常量,通常是大写。
{
type: 'ADD',
payload: '测试'
}
{
type: 'ADD',
payload: {
text: 'do something'
}
}
{
type: 'ADD',
payload: new Error(),
errro: true
}
除了 type
字段外,action 对象的结构完全有我们自己决定。
在 reducer 里通过收到的 action 的不同 type 值,去执行不同的修改 state 的行为。
通常来说,同步action 设置 type 就够了。异步action 除了必须的 type 属性外,我们还可以添加 payload、error、meta 等属性。
当系统中的action 越来越多时,建议把 action 单独抽出成一个文件。
如下面代码实例所示:
store/action/config.js
export const INCREMENT = 'INCREMENT',
export const DECREMENT = 'DECREMENT'
store/action/index.js
import * as constant from './config'
export const incrementNum = () => ({
type: constant.INCREMENT,
})
export const decrementNum = () => ({
type: constant.DECREMENT,
})
// 优化之前
addNum = () => {
store.dispatch({type: 'INCREMENT'})
}
decreNum = () => {
store.dispatch({type: 'DECREMENT'})
}
// 优化之后
addNum = () => {
store.dispatch(actions.incrementNum())
}
decreNum = () => {
store.dispatch(actions.decrementNum())
}
reducer
当 dispatch 发起了一个 action 后,会来到 reducer
reducer 就是用来计算新的 store 的,描述action 如何改变 state 树。
reducer 是一个纯函数,接收两个参数,参数为 当前state 和 action,返回一个新的 state。
store 在收到 action 之后,传递给 reducer,reducer 计算出新的 state 后返回,view 才会发生变化。这种state 的计算过程就叫做 reducer。
注意:在首次使用 redux时,我们需要给 state 一个默认值
// 这就是一个 reducer, 这个是 state 是基本类型
const counter = (state = 0, action) => {
switch(action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
// 当 state 为引用类型时
const initState = {
number: 0
}
const counter = (state: initValue, action) => {
switch(action.type) {
case 'INCREMENT':
return {
...state,
number: state + 1
}
case 'DECREMENT':
return {
...state,
number: state - 1
}
default:
return state
}
}
let store = createStore(counter)
combineReducers
随着应用变的越来越复杂,我们可以考虑将 reducer 函数拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分,此时我们就需要用到 combineReducers。
combineReducers
辅助函数的作用是:把一个由多个不同的 reducer 函数作为 value 的 object,合成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore
方法。
举一个比较好的例子
reducers.js
// reducers.js
export default theDefaultReducer = (state = 0, action) => {
switch(action.type) {
case 'ADD':
return state + 1
case 'DEC':
return state - 1
default:
return state
}
}
export firstReducer = (state = 1, action) => {
switch(action.type) {
case 'ADD':
return state + 1
case 'DEC':
return state - 1
default:
return state
}
}
export secondReducer = (state = 2, action) => {
switch(action.type) {
case 'ADD':
return state + 1
case 'DEC':
return state - 1
default:
return state
}
}
rootReducer.js
// 根reducer
import { combineReducers, createStore } from 'redux';
import theDefaultReducer, { firstReducer, secondReducer } from './reducers'
// 使用了 ES6 的对象字面量简写方式定义对象结构
const rootReducer = combineReducers({
theDefaultReducer,
firstReducer,
secondReducer
})
const store = createStore(rootReducer);
console.log(store.getState())
// { theDefaultReducer: 0, firstReducer: 1, secondReducer: 2 }
看到这里,相信你会对 redux 有一个初步的认知,不会在向以前一样摸不到头脑。
不过 redux 的知识可不止这一些,还有很多用法在这里没有说到,之后有机会我再往上补充吧,或者去学习官网,官网才是最权威的地方。