1 什么是Redux
Redux是一个流行的JavaScript框架,为应用程序提供一个可预测的状态容器。Redux基于简化版本的Flux框架,Flux是Facebook开发的一个框架。在标准的MVC框架中,数据可以在UI组件和存储之间双向流动,而Redux严格限制了数据只能在一个方向上流动。
在Redux中,所有的数据(比如state)被保存在一个被称为store的容器中 → 在一个应用程序中只能有一个。store本质上是一个状态树,保存了所有对象的状态。任何UI组件都可以直接从store访问特定对象的状态。要通过本地或远程组件更改状态,需要分发一个action。分发在这里意味着将可执行信息发送到store。当一个store接收到一个action,它将把这个action代理给相关的reducer。reducer是一个纯函数,它可以查看之前的状态,执行一个action并且返回一个新的状态。
2 配置Redux
配置Redux开发环境的最快方法是使用create-react-app工具。在开始之前,确保已经安装并更新了nodejs,npm和yarn。
yarn add redux
3 Action
Action 是把数据从应用(译者注:这里之所以不叫 view 是因为这些数据有可能是服务器响应,用户输入或其它非 view 的数据 )传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。
actions.js
// 创建一个任务
export const ADD = "ADD_NUMBER";
// 创建一个action
export const ADD_NUMBER = {
type:ADD,
sun:0
}
Action 本质上是 JavaScript 普通对象。我们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。多数情况下,type 会被定义成字符串常量。当应用规模越来越大时,建议使用单独的模块或文件来存放 action。除了 type 字段外,action 对象的结构完全由你自己决定。我们应该尽量减少在 action 中传递的数据。
4 Action 创建函数
Action 创建函数 就是生成 action 的方法。“action” 和 “action 创建函数” 这两个概念很容易混在一起,使用时最好注意区分。
在 Redux 中的 action 创建函数只是简单的返回一个 action:
actions.js
// 创建一个任务
export const ADD = "ADD_NUMBER";
// 创建一个action
export function addNum(sun) {
return {
type:ADD,
sun
}
}
5 Reducer
Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。
1 设计 State 结构
在 Redux 应用中,所有的 state 都被保存在一个单一对象中。
stores.js
export const stateData = {
sunObj:{
sun:0
}
}
2 Action 处理
确定了 state 对象的结构,就可以开始开发 reducer。reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。
(previousState, action) => newState
之所以将这样的函数称之为reducer,是因为这种函数与被传入 Array.prototype.reduce(reducer, ?initialValue) 里的回调函数属于相同的类型。保持 reducer 纯净非常重要。永远不要在 reducer 里做这些操作:
修改传入参数;
执行有副作用的操作,如 API 请求和路由跳转;
调用非纯函数,如 Date.now() 或 Math.random()。
我们将以指定 state 的初始状态作为开始。Redux 首次执行时,state 为 undefined,此时我们可借机设置并返回应用的初始 state。
index.js
import {ADD} from "./actions";
import {stateData} from "./stores";
function updateSun(state = stateData.sunObj, action) {
switch (action.type) {
case ADD:
return state.sun + action.sun
default:
return state
}
}
注意:
- 不要修改 state。
- 在 default 情况下返回旧的 state。遇到未知的 action 时,一定要返回旧的 state。
-
每个 reducer 只负责管理全局 state 中它负责的一部分。每个 reducer 的 state 参数都不同,分别对应它管理的那部分 state 数据。
3 处理多个 action
,Redux 提供了 combineReducers() 来注册多个Reducer
index.js import {ADD} from "./actions"; import {stateData} from "./stores"; import {createStore,combineReducers} from "redux"; function updateSun(state = stateData.sunObj, action) { switch (action.type) { case ADD: return state.sun + action.sun default: return state } } let reducers = combineReducers({updateSun})
6 Store
Store 就是把它们联系到一起的对象。Store 有以下职责:
- 维持应用的 state;
- 提供 getState() 方法获取 state;
- 提供 dispatch(action) 方法更新 state;
- 通过 subscribe(listener) 注册监听器;
-
通过 subscribe(listener) 返回的函数注销监听器
再次强调一下 Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合 而不是创建多个 store。
将创建好的reducer传入到createStore中=import {ADD} from "./actions"; import {stateData} from "./stores"; import {createStore,combineReducers} from "redux"; function updateSun(state = stateData.sunObj, action) { switch (action.type) { case ADD: return state.sun + action.sun default: return state } } let reducers = combineReducers({updateSun}) export const store = createStore(reducers)
createStore() 的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。
let store = createStore(todoApp, window.STATE_FROM_SERVER)
7 发起 Actions
import {ADD} from "./actions";
import {stateData} from "./stores";
import {createStore,combineReducers} from "redux";
import {addNum} from "./actions";
function updateSun(state = stateData.sunObj, action) {
switch (action.type) {
case ADD:
return {
sun: state.sun + action.sun
}
default:
return state
}
}
let reducers = combineReducers({updateSun})
export const store = createStore(reducers)
// 打印初始状态
console.log(store.getState())
// 每次 state 更新时,打印日志
// 注意 subscribe() 返回一个函数用来注销监听器
let unsubscribe = store.subscribe( e => {
console.log(store.getState())
})
// 发起一系列 action
setInterval(() => {
store.dispatch(addNum(1))
},2000)
// 停止监听 state 更新
unsubscribe()
1 在组件中使用
my.js
import React from 'react'
import {store} from "../../redux";
import {addNum} from "../../redux/actions";
class My extends React.Component{
constructor() {
super();
this.add = this.add.bind(this)
}
add(){
store.dispatch(addNum(1))
}
render() {
return (
<div>
<p>wide发大发过芽给发芽时</p>
<button onClick={this.add}>点击确认</button>
</div>
)
}
}
export default My
uswr.js
import React from 'react'
import {store} from "../../redux";
class User extends React.Component {
constructor() {
super();
this.state = {
text : 0
}
}
componentDidMount(){
this.setState({
text: store.getState().updateSun.sun
})
}
render() {
return (
<div>{this.state.text}</div>
)
}
}
export default User