1、redux
1.1)Action Creator
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
----
import * as types from '../action-types';
//actionCreator 建立action的函式
export default {
increment(){
return {type:types.INCREMENT}
},
decrement(){
return {type:types.DECREMENT}
}
}
複製程式碼
看著寫了這麼多,其實就是為了拿到字串INCREMENT
和DECREMENT
。
1.2)reducer
reducer是一個純函式,相同的輸入有相同的輸出,不同的輸入得到不同的輸出。它必須遵守以下幾點
- 不得改寫引數
- 不能呼叫系統 I/O 的API
- 不能呼叫Date.now()或者Math.random()等不純的方法,因為每次會得到不一樣的結果
reducer中不能改變state,在保證舊的狀態不變的情況下,返回一個全新的狀態
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
let initState = { number: 0 }
export default function (state = initState, action) {
switch (action.type) {
case INCREMENT:
return { number: state.number + 1 };
case DECREMENT:
return { number: state.number - 1 };
default:
return state;
}
}
複製程式碼
其實就相當於state變成了一個只是可讀的狀態,不可更改,返回了計算後的新的狀態,原狀態不變。
1.3)createStore
const createStore = (reducer) => {
let state;
let listeners = [];
const getState = () => state;// 因為dispatch的執行,拿到的是初始狀態
//訂閱,供外界訂閱本倉庫中狀態的變化 ,如果狀態變化 了會執行訂閱的邏輯
const subscribe = (listener) => {
listeners.push(listener);
return () => {//subscribe的返回結果是一個unsubscribe
listeners = listeners.filter(l => l !== listener);
}
};
const dispatch = (action) => {
//接收新的動作後,通過 才狀態 和新動作計算出新狀態
state = reducer(state, action);
listeners.forEach(listener => listener());
};
dispatch({});
// 這裡執行dispatch的原因是,為了執行reducer函式,最終獲取到裡面的初始 state,也就是說createStore()執行後,立馬拿到初始狀態。
return { getState, dispatch, subscribe };
};
複製程式碼
1.4)combinReducers
將兩個reducer合併
function combineReducers(reducers) {
//返回合併後的reducer,這個函式將被傳入createStore
return function (state = {}, action) {
let newState = {};
for (let attr in reducers) {
let reducer = reducers[attr];
newState[attr] = reducer(state[attr], action);
}
return newState;
}
}
export default combineReducers({
reducer1,
reducer2
});
複製程式碼
另外一種寫法:
export default reducers => (state = {}, action) => Object.keys(reducers).reduce((currentState, key) => {
currentState[key] = reducers[key](state[key], action);
return currentState;
}, {});
複製程式碼
1.5)bindActionCreator
我們做下面一個操作
<button onClick={()=>store.dispatch(actions.increment())}>-</button>
複製程式碼
bindActionCreator
用來把action
和dispatch
繫結在一起,讓書寫變得更方便
<button onClick={newActions.increment}>-</button>
複製程式碼
這是bindActionCreators
的實現原理:
function bindActionCreators(actions,dispatch){
let newActions = {};
for(let attr in actions){
newActions[attr] = function(){
dispatch(actions[attr].apply(null,arguments));
}
}
return newActions;
}
複製程式碼
1.6) 我們可以來生成redux了
import createStore from './createStore';
import combineReducers from './combineReducers';
import bindActionCreators from './bindActionCreators';
export {
createStore,
combineReducers,
bindActionCreators
}
複製程式碼
二、react-redux
1、Provider
provider
的作用是讓每個元件都擁有呼叫store
的能力
Provider
本身並沒有做很多事情,只是把store
放在context
裡罷了。實際上如果你用react-redux
,那麼連線檢視和資料層最好的辦法是使用connect
函式。本質上Provider
就是給connect
提供store
用的。
//用來通過上下文物件向下層元件傳遞資料 store
import React,{Component} from 'react';
import propTypes from 'prop-types';
export default class Provider extends Component{
static childContextTypes = {
store:propTypes.object.isRequired
}
getChildContext(){
return {store:this.props.store};
}
render(){
return this.props.children;
}
}
複製程式碼
一般我們在跟元件上使用
const store = configureStore();
render(
<Provider store={store}>
<Root />
</Provider>,
document.getElementById('root')
);
複製程式碼
2、connect
React-Redux
提供connect
方法,用於從 UI
元件生成容器元件。connect
的意思,就是將這兩種元件連起來。
作用:
-
(1)輸入邏輯:外部的資料(即
state
物件)如何轉換為UI
元件的引數 -
(2)輸出邏輯:使用者發出的動作如何變為
Action
物件,從UI
元件傳出去
connect
的使用
import { connect } from 'react-redux'
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
複製程式碼
connect
有兩個引數,分別是 mapStateToProps
,mapDispatchToProps
。
第一個引數負責輸入邏輯,即將state
對映到 UI
元件的引數(props
),第二個引數負責輸出邏輯,即將使用者對UI
元件的操作對映成 Action
。
2.1 ) mapStateToProps
返回值,是一個物件.
從字面上理解,就是把狀態(store
中的state
)對映成屬性(元件中的props
)
const mapStateToProps = (state) => ( // 正常我們在react-redux中會這樣書寫
{number: state.number}
)
複製程式碼
2.2)mapDispatchToProps
mapDispatchToProps
是connect
函式的第二個引數,用來建立 UI
元件的引數到store.dispatch
方法的對映。也就是說,它定義了哪些使用者的操作應該當作 Action
,傳給 Store
。它可以是一個函式,也可以是一個物件。
如果mapDispatchToProps
是一個函式,會把dispatch
當做一個引數傳進去。
const mapDispatchToProps = (dispatch) => {
return {
onClick: () => {
dispatch({
type: 'ADD'
});
}
};
}
// 返回了一個物件,該物件的每個鍵值對都是一個對映,定義了 UI 元件的引數怎樣發出 Action。
複製程式碼
如果mapDispatchToProps
是一個物件,它的每個鍵名也是對應 UI 元件的同名引數,鍵值應該是一個函式,會被當作 Action creator ,返回的 Action 會由 Redux 自動發出。
bindActionCreators(mapDispatchToProps,this.store.dispatch)
複製程式碼
2.3) connect 的實現
在connect中實現了元件的更新操作。
import React,{Component} from 'react';
import {bindActionCreators} from 'redux';
import propTypes from 'prop-types';
export default function(mapStateToProps,mapDispatchToProps){
return function(WrapedComponent){
class ProxyComponent extends Component{
static contextTypes = {
store:propTypes.object
}
constructor(props,context){
super(props,context);
this.store = context.store;//這裡的store是從Prodiver傳過來的
this.state = mapStateToProps(this.store.getState());//{color:state.color}
}
componentWillMount(){
this.unsubscribe = this.store.subscribe(()=>{
this.setState(mapStateToProps(this.store.getState()));// 根據store中的state來更新檢視。
});
}
componentWillUnmount(){
this.unsubscribe();
}
render(){
let actions= {};
if(typeof mapDispatchToProps == 'function'){
actions = mapDispatchToProps(this.store.disaptch);
}else if(typeof mapDispatchToProps == 'object'){
actions = bindActionCreators(mapDispatchToProps,this.store.dispatch);
}
return <WrapedComponent {...this.state} {...actions}/>
}
}
return ProxyComponent;
}
}
複製程式碼
2.4) 最後我們來生成react-redux
import connect from './connect';
import Provider from './Provider';
export {
connect,
Provider
}
複製程式碼