Ref: Redux中文文档javascript
Ref: React 讀書會 - B團 - Level 19 Redux 深刻淺出html
Ref: React+Redux 分享會前端
Ruan Yifeng, Redux 架构: 教程一(有代码)、教程二、教程三java
Redux 入门教程 #1 课程介绍react
Redux 入门教程 #3 什么是 Reduxgithub
Redux 入门教程 #4 建立页面mongodb
Redux 入门教程 #6 使用 react-reduxredux
(1) 首先,复习 Flux:[React] 07 - Flux: react communicates with mongodb
详见Flux章节和连接。
(2) 以后,让咱们开始对 Redux 认真学习。
2015年,Redux 出现,将 Flux 与函数式编程结合一块儿,很短期内就成为了最热门的前端架构。
* 是什么?
- actions
- components
- constants
- reducers
- store
管理数据的状态容器,单独的js库。
* 解决什么问题?
A如何把参数传给B?react的单向流很差处理。
或者,使用嵌入式写法,好比A触发事件,B监听事件。
但嵌入式写法不推荐。
- 代码结构
- 组件之间的通讯
- 用户的使用方式复杂
- 不一样身份的用户有不一样的使用方式(好比普通用户和管理员)
- 多个用户之间能够协做
- 与服务器大量交互,或者使用了WebSocket
- View要从多个来源获取数据
- 某个组件的状态,须要共享
- 某个状态须要在任何地方均可以拿到
- 一个组件须要改变全局状态
- 一个组件须要改变另外一个组件的状态
第一步,从老阮开始。设计一个以下的"加减计数器"。
[1] ----------------------------------------------------------------------------------------------------------------------------------------> Store & State
两个重要的功能:
(1)建立惟一的一个store。
(2)获取该store里面的state。
import { createStore } from 'redux';
const store = createStore(reducer); // 1.接受另外一个函数做为参数,返回新生成的 Store 对象
const state = store.getState(); // 2.获得当前时刻的State,一个state对应一个view
Jeff: 在flux中的state计算问题放在了reducer中,reducer与store经过createStore()维持关系。
[2] ----------------------------------------------------------------------------------------------------------------------------------------> Action
用户操做 on view --> action --> state
# 想要的结果
const action = {
type: 'ADD_TODO', // Action的名称,是必须的;Action自己是个对象
payload: 'Learn Redux' // Action的货物,不是必须的;
};
使用 Action Creator,避免所有“手写定义”带来的麻烦,返回以上的结果。
const ADD_TODO = '添加 TODO';
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
const action = addTodo('Learn Redux');
[3] ----------------------------------------------------------------------------------------------------------------------------------------> store.dispatch()
经过store.dispatch (...),view 发出 action (参数) to store.
store.dispatch({
type : 'ADD_TODO',
payload: 'Learn Redux'
});
或者,对象经过函数生成:
store.dispatch( // <--- 推荐
addTodo('Learn Redux')
);
[4] ----------------------------------------------------------------------------------------------------------------------------------------> Reducer
Store 收到 Action 之后,必须给出一个新的 State,View 才能发生变化。
Reducer:新State 的计算过程,based on old state。
const defaultState = 0;
const reducer = (state = defaultState, action) => {
/**
* reducer 函数里面不能改变 State,必须返回一个全新的对象
*/
switch (action.type) {
case 'ADD':
return state + action.payload;
default:
return state;
}
};
....................................................
const state = reducer(1, {
type: 'ADD',
payload: 2
});
由于createStore(reducer),每当store.dispatch
发送过来一个新的 Action,就会自动调用 Reducer,获得新的 State。
reducer 做为参数,为 "action们" 提供趁手的工具。
const actions = [
{ type: 'ADD', payload: 0 },
{ type: 'ADD', payload: 1 },
{ type: 'ADD', payload: 2 }
];
/**
* action是数据,打算使用reducer这个工具来处理,最后返回一个新的状态
*/
const total = actions.reduce(reducer, 0); // 3
另外:最好把 State 对象设成只读,这样你无法改变它,要获得新的 State,惟一办法就是生成一个新对象。
// State 是一个对象
function reducer(state, action) {
return Object.assign({}, state, { thingToChange });
// 或者
return { ...state, ...newState };
}
// State 是一个数组
function reducer(state, action) {
return [...state, newItem];
}
[5] ---------------------------------------------------------------------------------------------------------------------------------------- listener
store改变,提醒 listener,触发view改变。
解除 listener 居然是利用其"返回值"。
let unsubscribe = store.subscribe(() =>
console.log(store.getState())
);
unsubscribe();
Store 提供了三个方法:
store 的三个部分,以下:
import { createStore } from 'redux';
let { subscribe, dispatch, getState } = createStore(reducer);
let store = createStore(todoApp, window.STATE_FROM_SERVER)
// 第二个参数表示:整个应用的状态初始值,会覆盖 Reducer 函数的默认初始值
【createStore内部实现暂不深究】
三种 Action 分别改变 State 的三个属性。
const chatReducer = (state = defaultState, action = {}) => {
const { type, payload } = action;
switch (type) {
............................................................. ADD_CHAT:chatLog属性
case ADD_CHAT:
return Object.assign({}, state, {
chatLog: state.chatLog.concat(payload)
});
............................................................. CHANGE_STATUS:statusMessage属性
case CHANGE_STATUS:
return Object.assign({}, state, {
statusMessage: payload
});
............................................................. CHANGE_USERNAME:userName属性
case CHANGE_USERNAME:
return Object.assign({}, state, {
userName: payload
});
default: return state;
}
};
const chatReducer = (state = defaultState, action = {}) => {
return {
chatLog: chatLog(state.chatLog, action), // 函数名字重复也碍眼,参数也不太想多写
statusMessage: statusMessage(state.statusMessage, action),
userName: userName(state.userName, action)
}
};
理由: 这种拆分与 React 应用的结构相吻合:一个 React 根组件由不少子组件构成。这就是说,子组件与子 Reducer 彻底能够对应。
combineReducers
方法,专门用于拆分这种写法有一个前提,就是 State 的属性名必须与子 Reducer 同名。
import { combineReducers } from 'redux';
/**
* 先找个地儿,定义各个子 Reducer 函数,
* 而后用这个方法,将它们合成一个大的 Reducer
*/
const chatReducer = combineReducers({
chatLog,
statusMessage,
userName
})
export default todoApp;
import { combineReducers } from 'redux'
import * as reducers from './reducers'
const reducer = combineReducers(reducers)
怎么改变状态?
如何触发界面更新?
这些都是Store的活儿!
* 首先,用户发出 Action --> store。
store.dispatch(action);
* 而后,Store 自动调用 Reducer,而且传入两个参数:[当前 State] & [收到的 Action],返回新的 State 。
let nextState = todoApp(previousState, action); // 产生新状态
* State 一旦有变化,Store 就会调用监听函数。
/* 设置监听函数 */ store.subscribe(listener);
/* 若是使用的是 React,出发方式以下 */
function listerner() { let newState = store.getState(); // 得到新状态 component.setState(newState); // 根据新状态更新界面 }
Clicked: 3 times
const render = () => ReactDOM.render(
/* 自定义Counter控件的使用 */ <Counter value ={store.getState()} # 得到new state onIncrement={() => store.dispatch({ type: 'INCREMENT' })} onDecrement={() => store.dispatch({ type: 'DECREMENT' })} />, rootEl )
在复杂的状况下,state会是如何?这里的例子有点过于简单,没有表现。
export default (state = 0, action) => { # 产生新状态
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
class Counter extends Component {
constructor(props) { super(props); this.incrementAsync = this.incrementAsync.bind(this); this.incrementIfOdd = this.incrementIfOdd.bind(this); }
////////////////////////////////////////////////////////
incrementIfOdd() { if (this.props.value % 2 !== 0) { this.props.onIncrement() } } incrementAsync() { setTimeout(this.props.onIncrement, 1000) }
////////////////////////////////////////////////////////
render() { const { value, onIncrement, onDecrement } = this.props return ( <p> Clicked: {value} times {' '} <button onClick={onIncrement}> + </button> {' '} <button onClick={onDecrement}> - </button> {' '} <button onClick={this.incrementIfOdd}> Increment if odd </button> {' '} <button onClick={this.incrementAsync}> Increment async </button> </p> ) } }