redux文档
英文文档:redux.js.org/
中文文档:www.redux.org.cn/
Github:github.com/reduxjs/red…
原理图:
常用方法
import { combineReducers } from 'redux'
三大原则
- 单一数据源
- State 是只读的
- 使用纯函数来执行修改
三大模块
- Action
- Action 是把数据从应用(译者注:这里之所以不叫 view 是因为这些数据有可能是服务器响应,用户输入或其它非 view 的数据 )传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过
store.dispatch()将 action 传到 store。
- Action 是把数据从应用(译者注:这里之所以不叫 view 是因为这些数据有可能是服务器响应,用户输入或其它非 view 的数据 )传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过
/*
* action 类型
*/
export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'
/*
* 其它的常量
*/
export const VisibilityFilters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMPLETED',
SHOW_ACTIVE: 'SHOW_ACTIVE'
}
/*
* action 创建函数
*/
export function addTodo(text) {
return { type: ADD_TODO, text }
}
export function toggleTodo(index) {
return { type: TOGGLE_TODO, index }
}
export function setVisibilityFilter(filter) {
return { type: SET_VISIBILITY_FILTER, filter }
}
- Reducer
- Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。
处理多个 action
还有两个 action 需要处理。就像我们处理 SET_VISIBILITY_FILTER 一样,我们引入 ADD_TODO 和 TOGGLE_TODO 两个actions 并且扩展我们的 reducer 去处理 ADD_TODO.
import {
ADD_TODO,
TOGGLE_TODO,
SET_VISIBILITY_FILTER,
VisibilityFilters
} from './actions'
...
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
case ADD_TODO:
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
default:
return state
}
}
拆分 Reducer
function todos(state = [], action) {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false
}
]
case TOGGLE_TODO:
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
default:
return state
}
}
function visibilityFilter(state = SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter
default:
return state
}
}
function todoApp(state = {}, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action)
}
}
注意每个 reducer 只负责管理全局 state 中它负责的一部分。每个 reducer 的 state 参数都不同,分别对应它管理的那部分 state 数据。
最后,Redux 提供了 combineReducers() 工具类来做上面 todoApp 做的事情,这样就能消灭一些样板代码了。有了它,可以这样重构 todoApp:
import { combineReducers } from 'redux'
const todoApp = combineReducers({
visibilityFilter,
todos
})
export default todoApp
注意上面的写法和下面完全等价:
export default function todoApp(state = {}, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action)
}
}
源码
reducers.js
import { combineReducers } from 'redux'
import {
ADD_TODO,
TOGGLE_TODO,
SET_VISIBILITY_FILTER,
VisibilityFilters
} from './actions'
const { SHOW_ALL } = VisibilityFilters
function visibilityFilter(state = SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter
default:
return state
}
}
function todos(state = [], action) {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false
}
]
case TOGGLE_TODO:
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
default:
return state
}
}
const todoApp = combineReducers({
visibilityFilter,
todos
})
export default todoApp
- Store
Store 就是把# Action和# Reducer 它们联系到一起的对象。Store 有以下职责:
- 维持应用的 state;
- 提供
getState()方法获取 state; - 提供
dispatch(action)方法更新 state; - 通过
subscribe(listener)注册监听器; - 通过
subscribe(listener)返回的函数注销监听器。
再次强调一下 Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合 而不是创建多个 store。
import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)
createStore() 的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。
let store = createStore(todoApp, window.STATE_FROM_SERVER)
发起 Actions
现在我们已经创建好了 store ,让我们来验证一下!虽然还没有界面,我们已经可以测试数据处理逻辑了。
import {
addTodo,
toggleTodo,
setVisibilityFilter,
VisibilityFilters
} from './actions'
// 打印初始状态
console.log(store.getState())
// 每次 state 更新时,打印日志
// 注意 subscribe() 返回一个函数用来注销监听器
const unsubscribe = store.subscribe(() =>
console.log(store.getState())
)
// 发起一系列 action
store.dispatch(addTodo('Learn about actions'))
store.dispatch(addTodo('Learn about reducers'))
store.dispatch(addTodo('Learn about store'))
store.dispatch(toggleTodo(0))
store.dispatch(toggleTodo(1))
store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED))
// 停止监听 state 更新
unsubscribe();
可以看到 store 里的 state 是如何变化的:
可以看到,在还没有开发界面的时候,我们就可以定义程序的行为。而且这时候已经可以写 reducer 和 action 创建函数的测试。不需要模拟任何东西,因为它们都是纯函数。只需调用一下,对返回值做断言,写测试就是这么简单。
源码
index.js
import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)
代码:
- 定义 action , reducer, store
import {createStore} from 'redux';
// 定义常量
export const ADD = "ADD";
export const MINUS = "MINUS";
function reducer(state={count: 0}, action) {
console.log('reducer -- action', action , state);
switch (action.type){
case ADD:
return {count: state.count + 1};
break;
case MINUS:
return {count: state.count - 1};
break;
default:
return state;
}
}
const store = createStore(reducer);
export default store;
- 组件使用
import React from "react";
// 导入store对象,通过该对象派发action
import store from "./store";
// 创建类组件,实现页面效果
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
// 将store中的最新的state赋给当前组件的state
// getState():获取最新的state tree
// count是在store.js文件中定义的,初始值为0
number: store.getState().count,
}
}
componentDidMount() { // 页面加载完成后执行该函数
// 对store的state进行订阅
// store.subscribe可以实现订阅,同时该方法会返回一个函数,用于取消订阅
// 设置this.unSubScribe为当前类的属性,表示可以取消订阅的对象
this.unSubScribe = store.subscribe(() => {
this.setState({
number: store.getState().count
},()=>{
this.forceUpdate();
})
})
}
componentWillUnmount() {
// 在组件卸载时取消订阅
this.unSubScribe();
// this.unSubScribe2();
}
render() {
return (
<div>
{/*页面上显示当前状态下的数值*/}
<p>number:{this.state.number}</p>
{/*当点击“加1”按钮时派发行为action:ADD*/}
<button onClick={() => store.dispatch({type: "ADD"})}>加1</button>
<button onClick={() => store.dispatch({type: "MINUS"})}>减1</button>
</div>
);
}
}
export default Counter;
-
- 定义 action
-
- reducer 初始化 store,并定义 reducer
-
- 创建store: import {createStore} from 'redux'; 定义 reducer, const store = createStore(reducer);
-
- getState 获取store 中的state: store.getState
-
- subscribe 订阅store: store.subscribe
-
- dispatch action 派发action 到 reducer: store.dispatch