commit
a72be37fcd
|
@ -187,7 +187,7 @@ describe('Redux adapter', () => {
|
|||
reduxStore.dispatch({ type: 'toggle' });
|
||||
reduxStore.dispatch({ type: 'toggle' });
|
||||
|
||||
expect(counter).toBe(3); // NOTE: first action is always store initialization
|
||||
expect(counter).toBe(2); // execute dispatch two times, applyMiddleware was called same times
|
||||
});
|
||||
|
||||
it('Should apply multiple enhancers', async () => {
|
||||
|
@ -226,7 +226,7 @@ describe('Redux adapter', () => {
|
|||
|
||||
reduxStore.dispatch({ type: 'toggle' });
|
||||
|
||||
expect(counter).toBe(2); // NOTE: first action is always store initialization
|
||||
expect(counter).toBe(1); // execute dispatch two times, applyMiddleware was called same times
|
||||
expect(lastAction).toBe('toggle');
|
||||
expect(middlewareCallList[0]).toBe('callCounter');
|
||||
expect(middlewareCallList[1]).toBe('lastFunctionStorage');
|
||||
|
|
|
@ -67,18 +67,50 @@ export function isPromise(obj: any): boolean {
|
|||
return isObject(obj) && typeof obj.then === 'function';
|
||||
}
|
||||
|
||||
export function isSame(x, y) {
|
||||
if (typeof Object.is !== 'function') {
|
||||
if (x === y) {
|
||||
// +0 != -0
|
||||
return x !== 0 || 1 / x === 1 / y;
|
||||
} else {
|
||||
// NaN == NaN
|
||||
return x !== x && y !== y;
|
||||
}
|
||||
} else {
|
||||
return Object.is(x, y);
|
||||
export function isSame(x: unknown, y: unknown): boolean {
|
||||
// 如果两个对象是同一个引用,直接返回true
|
||||
if (x === y) {
|
||||
return true;
|
||||
}
|
||||
// 如果两个对象类型不同,直接返回false
|
||||
if (typeof x !== typeof y) {
|
||||
return false;
|
||||
}
|
||||
// 如果两个对象都是null或undefined,直接返回true
|
||||
if (x == null || y == null) {
|
||||
return true;
|
||||
}
|
||||
// 如果两个对象都是基本类型,比较他们的值是否相等
|
||||
if (typeof x !== 'object') {
|
||||
return x === y;
|
||||
}
|
||||
// 如果两个对象都是数组,比较他们的长度是否相等,然后递归比较每个元素是否相等
|
||||
if (Array.isArray(x) && Array.isArray(y)) {
|
||||
if (x.length !== y.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < x.length; i++) {
|
||||
if (!isSame(x[i], y[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// 两个对象都是普通对象,首先比较他们的属性数量是否相等,然后递归比较每个属性的值是否相等
|
||||
if (typeof x === 'object' && typeof y === 'object') {
|
||||
const keys1 = Object.keys(x!).sort();
|
||||
const keys2 = Object.keys(y!).sort();
|
||||
if (keys1.length !== keys2.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < keys1.length; i++) {
|
||||
if (!isSame(x![keys1[i]], y![keys2[i]])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getDetailedType(val: any) {
|
||||
|
|
|
@ -27,12 +27,12 @@ export {
|
|||
createDispatchHook,
|
||||
} from './reduxReact';
|
||||
|
||||
export type ReduxStoreHandler = {
|
||||
reducer: (state: any, action: { type: string }) => any;
|
||||
dispatch: (action: { type: string }) => void;
|
||||
getState: () => any;
|
||||
subscribe: (listener: () => void) => () => void;
|
||||
replaceReducer: (reducer: (state: any, action: { type: string }) => any) => void;
|
||||
export type ReduxStoreHandler<T = any> = {
|
||||
reducer(state: T, action: { type: string }): any;
|
||||
dispatch(action: { type: string }): void;
|
||||
getState(): T;
|
||||
subscribe(listener: () => void): () => void;
|
||||
replaceReducer(reducer: (state: T, action: { type: string }) => any): void;
|
||||
};
|
||||
|
||||
export type ReduxAction = {
|
||||
|
@ -53,6 +53,9 @@ export type ReduxMiddleware = (
|
|||
|
||||
type Reducer = (state: any, action: ReduxAction) => any;
|
||||
|
||||
type StoreCreator = (reducer: Reducer, preloadedState?: any) => ReduxStoreHandler;
|
||||
type StoreEnhancer = (next: StoreCreator) => StoreCreator;
|
||||
|
||||
function mergeData(state, data) {
|
||||
if (!data) {
|
||||
state.stateWrapper = data;
|
||||
|
@ -87,7 +90,7 @@ function mergeData(state, data) {
|
|||
state.stateWrapper = data;
|
||||
}
|
||||
|
||||
export function createStore(reducer: Reducer, preloadedState?: any, enhancers?): ReduxStoreHandler {
|
||||
export function createStore(reducer: Reducer, preloadedState?: any, enhancers?: StoreEnhancer): ReduxStoreHandler {
|
||||
const store = createStoreX({
|
||||
id: 'defaultStore',
|
||||
state: { stateWrapper: preloadedState },
|
||||
|
@ -130,12 +133,14 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?):
|
|||
dispatch: store.$a.dispatch,
|
||||
};
|
||||
|
||||
enhancers && enhancers(result);
|
||||
|
||||
result.dispatch({ type: 'InulaX' });
|
||||
|
||||
store.reduxHandler = result;
|
||||
|
||||
if (typeof enhancers === 'function') {
|
||||
return enhancers(createStore)(reducer, preloadedState);
|
||||
}
|
||||
|
||||
return result as ReduxStoreHandler;
|
||||
}
|
||||
|
||||
|
@ -150,19 +155,23 @@ export function combineReducers(reducers: { [key: string]: Reducer }): Reducer {
|
|||
};
|
||||
}
|
||||
|
||||
function applyMiddlewares(store: ReduxStoreHandler, middlewares: ReduxMiddleware[]): void {
|
||||
middlewares = middlewares.slice();
|
||||
middlewares.reverse();
|
||||
let dispatch = store.dispatch;
|
||||
middlewares.forEach(middleware => {
|
||||
dispatch = middleware(store)(dispatch);
|
||||
});
|
||||
store.dispatch = dispatch;
|
||||
function applyMiddlewares(createStore: StoreCreator, middlewares: ReduxMiddleware[]): StoreCreator {
|
||||
return (reducer, preloadedState) => {
|
||||
middlewares = middlewares.slice();
|
||||
middlewares.reverse();
|
||||
const storeObj = createStore(reducer, preloadedState);
|
||||
let dispatch = storeObj.dispatch;
|
||||
middlewares.forEach(middleware => {
|
||||
dispatch = middleware(storeObj)(dispatch);
|
||||
});
|
||||
storeObj.dispatch = dispatch;
|
||||
return storeObj;
|
||||
};
|
||||
}
|
||||
|
||||
export function applyMiddleware(...middlewares: ReduxMiddleware[]): (store: ReduxStoreHandler) => void {
|
||||
return store => {
|
||||
return applyMiddlewares(store, middlewares);
|
||||
export function applyMiddleware(...middlewares: ReduxMiddleware[]): (createStore: StoreCreator) => StoreCreator {
|
||||
return createStore => {
|
||||
return applyMiddlewares(createStore, middlewares);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -170,7 +179,7 @@ type ActionCreator = (...params: any[]) => ReduxAction;
|
|||
type ActionCreators = { [key: string]: ActionCreator };
|
||||
export type BoundActionCreator = (...params: any[]) => void;
|
||||
type BoundActionCreators = { [key: string]: BoundActionCreator };
|
||||
type Dispatch = (action) => any;
|
||||
type Dispatch = (action: ReduxAction) => any;
|
||||
|
||||
export function bindActionCreators(actionCreators: ActionCreators, dispatch: Dispatch): BoundActionCreators {
|
||||
const boundActionCreators = {};
|
||||
|
@ -183,12 +192,12 @@ export function bindActionCreators(actionCreators: ActionCreators, dispatch: Dis
|
|||
return boundActionCreators;
|
||||
}
|
||||
|
||||
export function compose(...middlewares: ReduxMiddleware[]) {
|
||||
return (store: ReduxStoreHandler, extraArgument: any) => {
|
||||
let val;
|
||||
middlewares.reverse().forEach((middleware: ReduxMiddleware, index) => {
|
||||
export function compose<T = StoreCreator>(...middlewares: ((...args: any[]) => any)[]): (...args: any[]) => T {
|
||||
return (...args) => {
|
||||
let val: any;
|
||||
middlewares.reverse().forEach((middleware, index) => {
|
||||
if (!index) {
|
||||
val = middleware(store, extraArgument);
|
||||
val = middleware(...args);
|
||||
return;
|
||||
}
|
||||
val = middleware(val);
|
||||
|
|
|
@ -41,29 +41,27 @@ export function createStoreHook(context: Context): () => ReduxStoreHandler {
|
|||
};
|
||||
}
|
||||
|
||||
export function createSelectorHook(context: Context): (selector?: (any) => any) => any {
|
||||
const store = createStoreHook(context)() as unknown as ReduxStoreHandler;
|
||||
return function (selector = state => state) {
|
||||
const [b, fr] = useState(false);
|
||||
export function createSelectorHook(context: Context): (selector?: ((state: unknown) => any) | undefined) => any {
|
||||
const store = createStoreHook(context)();
|
||||
return function useSelector(selector = state => state) {
|
||||
const [state, setState] = useState(() => store.getState());
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = store.subscribe(() => fr(!b));
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
});
|
||||
const unsubscribe = store.subscribe(() => {
|
||||
setState(store.getState());
|
||||
});
|
||||
return () => unsubscribe();
|
||||
}, []);
|
||||
|
||||
return selector(store.getState());
|
||||
return selector(state);
|
||||
};
|
||||
}
|
||||
|
||||
export function createDispatchHook(context: Context): () => BoundActionCreator {
|
||||
const store = createStoreHook(context)() as unknown as ReduxStoreHandler;
|
||||
return function () {
|
||||
return action => {
|
||||
store.dispatch(action);
|
||||
};
|
||||
}.bind(store);
|
||||
const store = createStoreHook(context)();
|
||||
return function useDispatch() {
|
||||
return store.dispatch;
|
||||
};
|
||||
}
|
||||
|
||||
export const useSelector = selector => {
|
||||
|
@ -94,7 +92,7 @@ type Connector<OwnProps, MergedProps> = (Component: OriginalComponent<MergedProp
|
|||
type ConnectOption<State = any> = {
|
||||
areStatesEqual?: (oldState: State, newState: State) => boolean;
|
||||
context?: Context;
|
||||
forwardRef?: boolean
|
||||
forwardRef?: boolean;
|
||||
}
|
||||
|
||||
export function connect<StateProps, DispatchProps, OwnProps, MergedProps>(
|
||||
|
@ -105,7 +103,7 @@ export function connect<StateProps, DispatchProps, OwnProps, MergedProps>(
|
|||
dispatchProps,
|
||||
ownProps
|
||||
): MergedProps => ({ ...stateProps, ...dispatchProps, ...ownProps } as unknown as MergedProps),
|
||||
options: ConnectOption,
|
||||
options?: ConnectOption
|
||||
): Connector<OwnProps, MergedProps> {
|
||||
if (!options) {
|
||||
options = {};
|
||||
|
@ -117,37 +115,31 @@ export function connect<StateProps, DispatchProps, OwnProps, MergedProps>(
|
|||
|
||||
//this component should mimic original type of component used
|
||||
const Wrapper: WrappedComponent<OwnProps> = (props: OwnProps) => {
|
||||
const [f, forceReload] = useState(true);
|
||||
|
||||
const store = useStore() as unknown as ReduxStoreHandler;
|
||||
const store = useStore() as ReduxStoreHandler;
|
||||
const [state, setState] = useState(() => store.getState());
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = store.subscribe(() => forceReload(!f));
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
});
|
||||
const unsubscribe = store.subscribe(() => {
|
||||
setState(store.getState());
|
||||
});
|
||||
return () => unsubscribe();
|
||||
}, []);
|
||||
|
||||
const previous = useRef({
|
||||
const previous = useRef<{ state: { [key: string]: any }; mappedState: StateProps }>({
|
||||
state: {},
|
||||
mappedState: {},
|
||||
}) as {
|
||||
current: {
|
||||
state: { [key: string]: any };
|
||||
mappedState: StateProps;
|
||||
};
|
||||
};
|
||||
mappedState: {} as StateProps,
|
||||
});
|
||||
|
||||
let mappedState: StateProps;
|
||||
if (options?.areStatesEqual) {
|
||||
if (options.areStatesEqual(previous.current.state, store.getState())) {
|
||||
if (options.areStatesEqual(previous.current.state, state)) {
|
||||
mappedState = previous.current.mappedState as StateProps;
|
||||
} else {
|
||||
mappedState = mapStateToProps ? mapStateToProps(store.getState(), props) : ({} as StateProps);
|
||||
mappedState = mapStateToProps ? mapStateToProps(state, props) : ({} as StateProps);
|
||||
previous.current.mappedState = mappedState;
|
||||
}
|
||||
} else {
|
||||
mappedState = mapStateToProps ? mapStateToProps(store.getState(), props) : ({} as StateProps);
|
||||
mappedState = mapStateToProps ? mapStateToProps(state, props) : ({} as StateProps);
|
||||
previous.current.mappedState = mappedState;
|
||||
}
|
||||
let mappedDispatch: DispatchProps = {} as DispatchProps;
|
||||
|
@ -156,12 +148,14 @@ export function connect<StateProps, DispatchProps, OwnProps, MergedProps>(
|
|||
Object.entries(mapDispatchToProps).forEach(([key, value]) => {
|
||||
mappedDispatch[key] = (...args: ReduxAction[]) => {
|
||||
store.dispatch(value(...args));
|
||||
setState(store.getState());
|
||||
};
|
||||
});
|
||||
} else {
|
||||
mappedDispatch = mapDispatchToProps(store.dispatch, props);
|
||||
}
|
||||
}
|
||||
mappedDispatch = Object.assign({}, mappedDispatch, { dispatch: store.dispatch });
|
||||
const mergedProps = (
|
||||
mergeProps ||
|
||||
((state, dispatch, originalProps) => {
|
||||
|
@ -169,13 +163,12 @@ export function connect<StateProps, DispatchProps, OwnProps, MergedProps>(
|
|||
})
|
||||
)(mappedState, mappedDispatch, props);
|
||||
|
||||
previous.current.state = store.getState();
|
||||
previous.current.state = state;
|
||||
|
||||
const node = createElement(Component, mergedProps);
|
||||
return node;
|
||||
return createElement(Component, mergedProps);
|
||||
};
|
||||
|
||||
if (options.forwardRef) {
|
||||
if (options?.forwardRef) {
|
||||
const forwarded = forwardRef((props, ref) => {
|
||||
return Wrapper({ ...props, ref: ref });
|
||||
});
|
||||
|
|
|
@ -35,5 +35,5 @@ function createThunkMiddleware(extraArgument?: any): ReduxMiddleware {
|
|||
}
|
||||
|
||||
export const thunk = createThunkMiddleware();
|
||||
// @ts-ignore
|
||||
thunk.withExtraArgument = createThunkMiddleware;
|
||||
|
||||
export const withExtraArgument = createThunkMiddleware;
|
||||
|
|
Loading…
Reference in New Issue