Skip to content

Commit

Permalink
feat(@wxa/redux): support reducer registry for lazy load model in sub…
Browse files Browse the repository at this point in the history
…package
  • Loading branch information
Genuifx committed Sep 10, 2019
1 parent e0e51ca commit c10bbe5
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 66 deletions.
153 changes: 96 additions & 57 deletions packages/wxa-redux/src/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
import mapState from './mapState'
import {diff} from '@wxa/core'
import mapState from './mapState';
import reducerRegistry from './registry';
import {diff} from '@wxa/core';

import {
createStore,
combineReducers,
applyMiddleware
} from 'redux'

const combine = (registryReducers, userReducers) => {
// if the user directly pass combined reducers to plugin, then we need to use it directly;
if (typeof userReducers === 'function') return userReducers;

// const reducerNames = Object.keys(reducers);
// Object.keys(initialState).forEach(item => {
// if (reducerNames.indexOf(item) === -1) {
// reducers[item] = (state = null) => state;
// }
// });
return combineReducers({...registryReducers, ...userReducers});
};

let mountRedux = function (originHook) {
return function (...args) {
this.$$reduxDiff = diff.bind(this);
Expand Down Expand Up @@ -44,68 +60,91 @@ let unmountRedux = function (originUnmount) {
}

export const wxaRedux = (options = {}) => {
let reducers;
let middlewares;
let isArray = false;
// get options.
let args = [];
let userReducers;
if (Array.isArray(options)) {
reducers = options[0];
isArray = true;
userReducers = options[0];
// object reducer
args = [combine(reducerRegistry.getReducers(), userReducers), ...options.slice(1)];
} else {
reducers = options.reducers;
middlewares = options.middlewares;
userReducers = options.reducers;
let {
middlewares,
initialState
} = options;

args = [combine(reducerRegistry.getReducers(), userReducers), initialState];
if (Array.isArray(middlewares)) args.push(applyMiddleware(...middlewares));
}

if(reducers == null) console.warn('不存在reducer, redux插件将无法工作。')
// create Store directly;
// cause the reducer may be attached at subpackages.
let store = createStore.apply(null, args);
reducerRegistry.setChangeListener((reducer)=>{
store.replaceReducer(combine(reducer, userReducers));
});

let syncStore = function(){
this.$$isCurrentPage = true;
let data = mapState(this.mapState, this.$store.getState(), this.$$storeLastState, this);
if (data != null) {
let diffData = this.$$reduxDiff(data)
this.setData(diffData)
};
}

return (vm, type) => {
if (type === 'App' && reducers) {
let args = options;
if (!isArray) {
args = [reducers];
if (Array.isArray(middlewares)) args.push(applyMiddleware(...middlewares));
}

vm.$store = createStore.apply(null, args);
} else if (type === 'Page') {
vm.$store = getApp().$store;
let {
onLoad,
onShow,
onUnload,
onHide
} = vm;
vm.onLoad = mountRedux(onLoad);
vm.onShow = function (...args) {
this.$$isCurrentPage = true;
let data = mapState(this.mapState, this.$store.getState(), this.$$storeLastState, this);
if (data != null) {
let diffData = this.$$reduxDiff(data)
this.setData(diffData)
};
if (onShow) onShow.apply(this, args);
}
vm.onHide = function (...args) {
this.$$isCurrentPage = false;
if (onHide) onHide.apply(this, args);
}
vm.onUnload = unmountRedux(onUnload);
} else if (type === 'Component') {
let {
created,
attached,
detached
} = vm;
vm.created = function (...args) {
this.$store = getApp().$store;
if (created) created.apply(this, args);
}
vm.$$isCurrentPage = true;
vm.attached = mountRedux(attached);
vm.detached = unmountRedux(detached);
} else {
throw new Error('不合法的wxa组件类型');
switch (type) {
case 'App':
vm.$store = store;
break;
case 'Page':
vm.$store = store;
let { onLoad, onShow, onUnload, onHide } = vm;
vm.onLoad = mountRedux(onLoad);
vm.onShow = function (...args) {
syncStore.bind(this)();
if (onShow) onShow.apply(this, args);
}
vm.onHide = function (...args) {
this.$$isCurrentPage = false;
if (onHide) onHide.apply(this, args);
}
vm.onUnload = unmountRedux(onUnload);
break;
case 'Component':
let {
created,
attached,
detached,
pageLifetimes
} = vm;
vm.pageLifetimes = pageLifetimes || {};
let {show, hide} = vm.pageLifetimes;
// auto sync store data to component.
vm.pageLifetimes.show = function(args) {
syncStore.bind(this)();
if (show) show.apply(this, args);
}
vm.pageLifetimes.hide = function(args) {
this.$$isCurrentPage = false;
if (hide) hide.apply(this, args);
}

vm.created = function (...args) {
this.$store = store;
if (created) created.apply(this, args);
}
vm.attached = mountRedux(attached);
vm.detached = unmountRedux(detached);
break;
default:
throw new Error('不合法的wxa组件类型');
}
};
}

export * from 'redux';
export * from 'redux';

export {reducerRegistry};
27 changes: 27 additions & 0 deletions packages/wxa-redux/src/registry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// http://nicolasgallagher.com/redux-modules-and-code-splitting/
// learn from nicolas

export class ReducerRegistry {
constructor() {
this._emitChange = null;
this._reducers = {};
}

getReducers() {
return { ...this._reducers };
}

register(name, reducer) {
this._reducers = { ...this._reducers, [name]: reducer };
if (this._emitChange) {
this._emitChange(this.getReducers());
}
}

setChangeListener(listener) {
this._emitChange = listener;
}
}

const reducerRegistry = new ReducerRegistry();
export default reducerRegistry;
51 changes: 42 additions & 9 deletions packages/wxa-redux/test/wxa-redux.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {wxaRedux, combineReducers} from '../src/index'
import {wxaRedux, combineReducers, reducerRegistry} from '../src/index'

getCurrentPages = function() {
return [{route: 'pages/index/index'}];
Expand Down Expand Up @@ -38,6 +38,7 @@ let todoDel = function(state=[], action) {

describe('App register redux', ()=>{
test('mount redux store and middlewares', ()=>{

let reduxFn = wxaRedux({
reducers: combineReducers({todo})
});
Expand Down Expand Up @@ -98,16 +99,16 @@ describe('App register redux', ()=>{
}).toThrowErrorMatchingSnapshot();
});

test('reducers is null', ()=>{
let warn = jest.fn();
let ogWarn = console.warn;
console.warn = warn;
// test('reducers is null', ()=>{
// let warn = jest.fn();
// let ogWarn = console.warn;
// console.warn = warn;

let reduxFn = wxaRedux();
expect(warn).toHaveBeenCalled();
// let reduxFn = wxaRedux();
// expect(warn).toHaveBeenCalled();

console.warn = ogWarn;
})
// console.warn = ogWarn;
// })
})

let app = {};
Expand Down Expand Up @@ -273,6 +274,7 @@ describe('Component use redux', ()=>{
expect(com.data).toMatchObject({});
expect(com.$unsubscribe).not.toBeFalsy();

com.pageLifetimes.show.bind(com)();
com.$store.dispatch({type: 'comAdd', payload: 'testcom'});
expect(com.data.todoList.length).toBe(1);
com.$store.dispatch({type: 'comAdd', payload: 'testcom'});
Expand All @@ -285,6 +287,37 @@ describe('Component use redux', ()=>{
})
})

describe('registry reducer', ()=>{
let reduxFn = wxaRedux({
reducers: {
todo
}
});

let page = {
setData
};

reduxFn(page, 'Page');

test("check reduers' amount", () => {
expect(page.$store).not.toBeFalsy();
expect(Object.keys(page.$store.getState()).length).toBe(1);
});

test("lazy register reducer", ()=>{
page.$store.dispatch({type: 'add', payload: 'xxx'});
expect(page.$store.getState().todo.length).toBe(1);

reducerRegistry.register('todoDel', todoDel);
reducerRegistry.register('comTodo', comTodo);
setTimeout(() => {
expect(Object.keys(page.$store.getState()).length).toBe(3);
expect(page.$store.getState().todo.length).toBe(1);
});
});
})

afterAll(()=>{
global.getApp = og;
})

0 comments on commit c10bbe5

Please sign in to comment.