之前在有道云笔记上写过一篇 Redux 学习实践的文章,附带了一个小 Demo。由于一段时间没看 Redux,加上之前缺乏足够的练习,现在想重构个人博客,使用 Redux 进行状态。现在不得不回头再看看 Redux 相关东西。这里顺便记录下学习笔记,一来便于回顾,二来希望对他人有所帮助。笔记同步写在个人博客网站
这篇文章是知识点的梳理,如果你对 Redux 的基础有一定了解,阅读起来可能比较合适。
什么是 Redux?
Redux 可以说是 Flux 架构思想的最佳实践方案,用于对大型、复杂、多数据来源的 React 进行数据管理。遵循 Redux 的设计原则,再配合 React 容器组件、展示组件分离的代码组织原则,可以让我们的项目结构清晰,数据流清晰,十分利于项目的维护开发。
Redux 设计的三大原则
- 单一的数据来源,所有的数据保存在一个对象中。
- 状态只读,Reducer 根据 store dispatch 的 action 类型返回一个新的状态,不修改原来的状态。
- 状态修改均由纯函数完成。
关于这三大原则的深入理解建议参考 《深入React技术栈》、《深入浅出React和Redux》
浅谈 Redux 的工作过程(和 MVC 的主要区别)
有 MVC 框架使用经验的同学应该清楚,MVC 的工作过程是这样:view 层将用户行为传递到 controller 中的action, controller 中的 action 和 Model 层交互操作数据,返回到 view。通常 MVC 框架的实现 M 和 V 层是可以交互的,这样数据就不是单向流动,这种设计很多场景下很灵活,写起来很爽快,但是要项目过大,项目数据流复杂,项目维护就不爽了。
Redux 则严格遵循上面提到的设计三大原则,工作过程是这样的:view 层触发一个 action,这个 action 由 store.dispatch(action) 传递给 reducer,reducer 根据 previousState 和 action type 返回一个新的 state(不修改数据), 如此实现了数据的变更。
Redux 核心 API
上面 Redux 的工作过程提到了 Redux 有 store、action、reducer 这些概念,这些都是 Redux 对外提供的核心 API,我们来看看他们分别是干嘛的。
-
store
store 作为整个应用数据的唯一来源,用于保存数据和 dispatch action。store 使用 Redux 提供的 createStore 方法来生成。
import { createStore } from 'redux';
let store = createStore(reducer)
createStore 接受一个 reducer 作为参数,返回一个 store 对象。store 对象包含 4 中方法:
getState():// 用于获取 store 中当前的状态
dispatch():// 用于分发 action,这个是改变 store 中数据的唯一方式
subscribe(listener):// 注册一个监听者,在 store 发生变化时被调用
replaceReducer(nextReducer):///更新当前 store 中的 reducer
reducer
reducer 本质上是一个函数,这个函数能接受一个 action 和一个旧的状态(previousState),根据 previousState 和 action 返回新的状态
reducer(previousState, action) => newState
reducer 必须是一个纯函数,不能修改原来状态,关于纯函数的概念请阅读其他资料学习。
-
action
reducer 的 action 本质上是一个 javaScript 对象,这个对象必须有一个 type 属性,用来表明 action 的类型,还可以有 payload, error, meta 属性。关于 action 对象的属性,社区有个规范,可以参考github.com/redux-utili…
const action = {
type: 'ADD_TODO',
payload: 'todo'
}
-
reducer 就是根据 action type 属性来决定返回新的 state。
先睹为快
我在学习 Redux 的时候,看了很多文档,认为对 Redux 的概念了然于胸,但是动手写第一个小 Demo 的时候仍然觉得很难下手,可能是 Redux 这些概念对我而言太新。这里给出我写的第一个小 Demo。
这个 Demo 实现了点击按钮让数字加一减一的功能,没有任何复杂度,单纯的是为了了解 action
、store
、reducer
的具体作用和协作关系。
Demo 地址: github.com/wewin11235/…
为了便于观察,Demo 的代码都丢在了一个文件里,在实际开发中,一定要遵循好的代码组织原则。
import React from 'react';
import ReactDOM from 'react-dom';
// 创建 action
// action 是 javaScript 对象,是用户行为的抽象,必须包含一个 stype 字段
// 根据 Demo 需要实现记数功能,抽象出两种 action, 一种表示加,一种表示减
const incream_action = { type: 'INCREAM' };
const decream_action = { type: 'DECREME' };
// 创建 reducer
// reducer 接受一个 previousState 和 action 返回一个新的 state
const reducer = (state=0, action) => {
switch (action.type) {
case incream_action.type:
return state + 1;
case decream_action.type:
return state - 1;
default:
console.log('default');
return state;
}
}
// 创建 store
// store 创建使用 createStore(reducers)
// createStore 由 redux 提供
import { createStore } from 'redux';
let store = createStore(reducer);
// 创建计数组件
class Counter extends React.Component {
constructor(props) {
super(props);
}
render() {
const { count, doIncrement, doDecrement } = this.props
return(
<div>
<span>{count}</span>
<button onClick={doIncrement}>+</button>
<button onClick={doDecrement}>-</button>
</div>
)
}
}
const render = () => ReactDOM.render(
<Counter
count={store.getState()}
doIncrement={() => store.dispatch(incream_action)}
doDecrement={() => store.dispatch(decream_action)}
/>,
document.getElementById('root')
);
render();
// 为了能在 store 发生变化后能刷新页面,需要给 store 注册一个监听事件
// store 变化后,刷新页面
store.subscribe(render);
最小 Redux 应用诞生。
欢迎指正,补充!