一個用于理解原理的簡寫版
redux
//redux有一下三個主要函數(shù)
//createStore
//getState 獲取當(dāng)前state
//dispatch 觸發(fā)state的更新
// 這個reducer是項目中state最終改變的地方
// 這里的enhancer是調(diào)用中間件的時候返回給我們的函數(shù),即applyMiddleWare(thunk)的返回值
export function createStore(reducer, enhancer) {
// 如果存在中間件的返回值扇调,就觸發(fā)creaestore矿咕,得到他返回的store。然后對dispatch做修改狼钮,最終作為新的store返回給provider
if(enhancer){
return enhancer(createStore)(reducer)
}
let currentState = {};
// currentListener 是訂閱事件存放的數(shù)組碳柱,每當(dāng)dispatch的時候,都出遍歷這個數(shù)組去觸發(fā)事件
let currentListener = [];
function getState() {
return currentState;
}
// 監(jiān)聽的事件從這里進(jìn)入
function subscribe(listener) {
currentListener.push(listener);
}
// 觸發(fā)state的改變熬芜,觸發(fā)監(jiān)聽的事件
function dispatch(action) {
// 觸發(fā)state的改變莲镣,state代表著整個app的狀態(tài),這個狀態(tài)是由我們維護(hù)的涎拉,是我們自己描述的
currentState = reducer(currentState,action);
// 遍歷監(jiān)聽數(shù)組中的所有監(jiān)聽事件
currentListener.forEach(v=>v());
return action;
}
// 在第一次createstore的時候瑞侮,currentState是應(yīng)當(dāng)有值的的圆,所以我們手動觸發(fā)第一次的dispatch,
// 因為state只能通過dispatch改變半火,這里傳遞一個非常特殊的type類型越妈,用來避開用戶編寫reducer時所設(shè)置的type
dispatch({type:'@@demoredux'});
return { getState, subscribe, dispatch }
}
export function applyMiddleWare(...middlewares) {
return createStore=>(...args)=>{
const store = createStore(...args);
let dispatch = store.dispatch;
const midApi = {
getState: store.getState,
dispatch: (...args)=>dispatch(...args)
};
// 所有操作的目標(biāo)都是state,而只有dispatch才可以改變state钮糖,所以梅掠,中間件就是對dispatch進(jìn)行一層封裝,以實現(xiàn)他想要達(dá)到的功能
// dispatch = middleware(midApi)(store.dispatch);
// 返回一個數(shù)組藐鹤,這個數(shù)組是由準(zhǔn)備接受next參數(shù)的函數(shù)構(gòu)成的
const middlewareChain = middlewares.map(middleware=>middleware(midApi));
dispatch = compose(...middlewareChain)(store.dispatch);
return {
...store,
dispatch
}
}
}
function compose(...funcs) {
if(funcs.length===0){
return arg=>arg
}
if(funcs.length===1){
return funcs[0]
}
// 獲取最后一個函數(shù)
const last = funcs[funcs.length - 1];
// 獲取除最后一個以外的函數(shù)[0,length-1)
const rest = funcs.slice(0, -1);
// 通過函數(shù) curry 化
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
// 新版redux-thunk中使用以下方式 reduce省略第二個參數(shù)的時候瓤檐,第二個參數(shù)的默認(rèn)值為數(shù)組中的第一個元素
// return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
// 從createStore返回的對象來看,state是不向外暴露的娱节,只通過store向外暴露的三個方法挠蛉,去獲取,監(jiān)聽以及觸發(fā)
function bindActionCreator(creator, dispatch) {
return (...args)=>dispatch(creator(...args));
}
// 這個工具函數(shù)用來處理action肄满,因為單獨執(zhí)行action是沒有任何效果的谴古,我們需要將所做動作的類型與改變內(nèi)容傳遞給dispatch才可以
export function bindActionCreators(creators, dispatch){
// 將包裹的結(jié)果放在bound里
let bound = {};
Object.keys(creators).forEach(v=>{
let creator = creators[v];
// 我思考過這里為什么不用箭頭函數(shù)去實現(xiàn),因為這里需要的是一個自執(zhí)行函數(shù)稠歉,用來給返回的函數(shù)提供一個作用域去存放dispatch和creator
// 如果寫成剪頭函數(shù)掰担,無法自執(zhí)行
bound[v] = bindActionCreator(creator, dispatch);
});
return bound;
// 可以用reduce進(jìn)行累加操作,更加的函數(shù)式
// return Object.keys(creators).reduce((res, item)=>{
// res[item] = bindActionCreator(creators[item], dispatch)
// },{});
}
react-redux
// context PropTypes,使用context怒炸,必須先定義PropTypes
// 首先在父組件內(nèi)定義
// static childContextTypes = {}
// getChildContext(){
// return 想要放入context內(nèi)部的東西
// }
// 然后带饱,在子組件內(nèi)部,首先定義proptypes
// static contextTypes = {}
// 使用this.context.*來獲取想要的東西
// 這個context阅羹,就是provider里存放store的地方
import React from 'react';
import PropTypes from 'prop-types';
import bindActionCreators from './redux';
class Provider extends React.Component{
static childContextTypes = {
store: PropTypes.object
};
//不清楚這里為什么傳入context
constructor(props, context){
super(props, context);
//因為store是我們調(diào)用createStore后勺疼,以props傳遞給provider的,所以在這里這樣獲取
this.store = props.store;
}
getChildContext(){
return { store: this.store };
}
render(){
return this.props.children;
}
}
// connect
const connect = (mapStateToProps=state=>state, mapDispatchToProps={})=>(Wrapcomponent)=>(
class ConnectComponent extends React.Component{
// 每一個組件都要獲取我們寫在context里的store
static contextTypes = {
store: PropTypes.object
};
constructor(props, context){
super(props, context);
this.state = {
props: {}
}
}
componentDidMount(){
// 不理解這里為什么要訂閱update捏鱼,我覺得mapStateToProps和mapDispatchToProps在寫定之后并不會改變
// 更新是為了update里的setState方法执庐,確保組件在dispatch之后可以更新,可是這樣子不就相當(dāng)于只要有更改
// 頁面所有組件都會強(qiáng)制刷新导梆,會有性能問題嗎
const { store } = this.context;
store.subscribe(()=>this.update());
this.update();
}
update(){
//獲取mapStateToProps和mapDispatchToProps 放入this.props里
const { store } = this.context;
//mapStateToProps和mapDispatchToprops都是由我們寫的轨淌,我們要告訴子組件,傳遞給他什么
const stateProps = mapStateToProps(store.getState());
const dispatchProps = bindActionCreators(mapDispatchToProps);
this.setState({
props: {
...this.state.props,
...stateProps,
...dispatchProps
}
})
}
render(){
return <Wrapcomponent {this.state.props}/>
};
}
);
export default { Provider }
thunk
const thunk = ({dispatch,getState})=>(next)=>(action)=>{
if(typeof action === 'function' ){
return action(dispatch, getState)
}
return next(action)
};
export default thunk