redux核心方法createStore
实现dispatch,getStore,subscribe等api
帮助方法
简单实现applyMiddleWare,combinReducers,bindActionCreatores,
中间件
thunk,logger
个人心得:此版进行浅比较,store未改变就不执行subscrube,瞎玩,store初始值要是一个简单对象
使用方法
代码语言:javascript复制import React, { Component } from 'react'
import store from './../store'
export default class MyReduxPage extends Component {
add = () => {
const { dispatch } = store;
// dispatch({ type: 'ADD', payload: 10 })
dispatch(() => {
return { type: 'ADD', payload: 10 }
})
}
sub = () => {
const { dispatch } = store;
dispatch({ type: 'SUB', payload: 10 })
}
addAge = () => {
const { dispatch } = store;
dispatch({ type: 'ADDAGE', payload: 40 })
}
componentDidMount() {
// 订阅事件
this.unSubscribe = store.subscribe(() => {
// 强制更新
this.forceUpdate()
})
}
componentWillUnmount() {
// 取消订阅
this.unSubscribe();
}
render() {
const { add, sub, addAge } = this;
console.log(4444)
return (
<div>
<h2>这厮是store</h2>
<div>{store.getStore().currentReducer.count}</div>
{/* 点击按钮就可以触发store.dispatch,然后调用reducer,改变store的值,但是,页面早已经渲染完了,页面展示的是初始的值,不会重新render */}
<button onClick={add}>点击 10</button>
{/* 所以我们必须订阅一个事件,相当于在改变数据后的回调,因为reducer执行完,会遍历subscribe事件 */}
<button onClick={sub}>点击-10</button>
<h3>这是age Store</h3>
<div>{store.getStore().ageReducer.age}</div>
<button onClick={addAge}>点击 40</button>
<button onClick={sub}>点击-10</button>
<br />
<button onClick={() => store.dispatch({ type: 'ssssss' })}>
dispatch个无操作的值
</button>
</div>
)
}
}
简单实现方法
代码语言:javascript复制// createStore方法
function createStore(reducer, enhancer) {
if (enhancer) {
// 如果应用了中间件,就要加强dispatch
return enhancer(createStore)(reducer)
}
// 定义一个store undefined
let store;
// 存储订阅事件
let subscrition = []
// 获取store方法
function getStore() {
return store
}
// 触发reducer方法
function dispatch(action) {
// 改变store后
const newStore = reducer(store, action);
// 触发订阅事件
if (newStore !== store) {
store = newStore;
subscrition.forEach(update => update())
}
}
// 订阅方法,返回一个取消订阅函数,有来有去,节约土地
function subscribe(update) {
subscrition.push(update)
return () => {
subscrition = subscrition.filter(subscribe => subscribe !== update)
}
}
// 手动触发一下dispatch,将用户的初始值,赋值给store
// 首次store无值,所以取默认值
// reudcer(store=9,action)
dispatch({ type: '@abdcdddsfasf' })
return {
getStore,
dispatch,
subscribe
}
}
// combineReducers 为了更好的实现reducer模块化,合并多个reducer方法
// 接收一个redcueObj,返回一个reducer函数
// 勿忘初心嘛1返回一个reducer函数
function combineReducers(reducerObj) {
// 获取reducer key值集合
const reducerKeys = Object.keys(reducerObj);
return (store = {}, action) => {
const nextStore = {};
// store是否改变了标识
let changed = false;
// 触发一个dispatch 遍历执行所有的reducer,因为reducer是纯函数嘛,没合适的类型返回原值
// 宁可错杀一万,不能放过一个的策略,挨个触发一下
reducerKeys.forEach(key => {
// 获取当前reducer
const currentReducer = reducerObj[key];
// 获取当前reducer对应的store
const currentStore = store[key];
// 执行这个reducer,传入这个reducer对应的store和action
nextStore[key] = currentReducer(currentStore, action);
// 改变标识,如果当前store不等于下次的store,标识为true
changed = changed || nextStore[key] !== currentStore;
})
// store对象里属性值得数量变化也是变化
changed = changed || Object.keys(nextStore).length !== Object.keys(store).length;
// 改变了?返回新的,否则返回老的
console.log(nextStore , 'store')
return changed ? nextStore : store;
}
}
// 为了满足复杂的业务场景,提升dispatch能力,让他不单纯的只会处理对象,也让能处理函数或promise等等,
// 应用中间件的方法,可能是多个,...接收
function applyMiddleWare(...middleWare) {
// 返回一个函数,这个函数做createStoer要干的活
// 所以传进来,加强dispatch
return createStore => reducer => {
// 这事不能不干,
let store = createStore(reducer);
// 下面开始加强store
// 首先给中间传入他们可能用到的参数
// dispatch,getStore
let dispatch = store.dispatch;
let api = {
getStore: store.getStore,
dispatch: (action, ...arg) => dispatch(action, ...arg)
}
// 遍历调用一下,闭包 、他们传入未来可能会用到的参数
const china = middleWare.map(middle => middle(api));
// 加强dispatch
// action => {
// if (typeof action === 'function') {
// return next(action())
// }
// return next(action)
// }
// next 是前一个中间件也是这个玩意,
// action => {
// if (typeof action === 'function') {
// return next(action())
// }
// return next(action)
// }
// 直到最后没有中间件了dispatch真身才真的遇到了action,现在为止,这个函数没调用呢
// 等到用户dispatch的时候才触发第一个中间件函数
// (...arg)=>fn3((...arg)=>fn2((...arg)=>fn1(...arg)))====> 恕在下无能,实在无法表达,层层闭包,
dispatch = compose(...china)(dispatch)
// 覆盖原来的dispatch
return {
...store,
dispatch
}
}
}
// compose函数
function compose(...china) {
// 判断长度,避免处理简单的和空的
if (china.length === 0) {
// 没有中间件,返回空函数,接收啥就返回啥
return arg => arg;
}
if (china.length === 1) {
// 就一个就返回这个函数就行了
// 类似,或者说就是这个functio
// 就是我要调用next(action)中间做一些事,实际就是dispatch(action)中间
// next => action => {
// // 判断action是否为函数
// if (typeof action === 'function') {
// return next(action())
// }
// return next(action)
// }
return china[0]
}
// 这个和上面一样
// next => action => {
//
// if (typeof action === 'function') {
// return next(action())
// }
// return next(action)
// }
// 每个fn都类似上面这个函数
// [fn1,fn2,fn3]====>(...arg)=>fn3((...arg)=>fn2((...arg)=>fn1(...arg)))
return china.reduce((prev, next) => (...arg) => next(prev(...arg)));
}
// 其他数据结构,所以按照自己需要应用中间件
// thunk, logger中间件简单实现
// 中间件能接收一个对象参数,里面有dispatch和getStore
function thunk({ dispatch, getStore }) {
return next => action => {
// 判断action是否为函数
console.log(3333)
if (typeof action === 'function') {
return dispatch(action())
}
return next(action)
}
}
function logger({ dispatch, getStore }) {
return next => action => {
// 打印pre store
console.log(getStore(), '之前的store');
let returnNext = next(action);
// 打印next store
console.log(getStore(), '改变后的store');
// 别忘了下一个中间件参数
return returnNext;
}
}
// reudecer纯函数
/*params
*store 原来store值 action,描述及payload参数
*/
const currentReducer = (store = { count: 0 }, action) => {
switch (action.type) {
case "ADD": return { count: store.count action.payload };
case "SUB": return { count: store.count - action.payload };
default: return store;
}
}
const ageReducer = (store = { age: 100 }, action) => {
switch (action.type) {
case "ADDAGE": return { age: store.age action.payload };
case "SUB": return { age: store.age - action.payload };
default: return store;
}
}
// 用法
// applyMiddleWare应用中间件
let store = createStore(combineReducers({ currentReducer, ageReducer }), applyMiddleWare(thunk, thunk, logger));
export default store;