!88 [inulax] 状态管理器inulax问题修复

Merge pull request !88 from xuan/main
This commit is contained in:
openInula-robot 2023-12-01 01:51:17 +00:00 committed by Gitee
commit a72be37fcd
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
5 changed files with 116 additions and 82 deletions

View File

@ -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');

View File

@ -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) {

View File

@ -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);

View File

@ -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 });
});

View File

@ -35,5 +35,5 @@ function createThunkMiddleware(extraArgument?: any): ReduxMiddleware {
}
export const thunk = createThunkMiddleware();
// @ts-ignore
thunk.withExtraArgument = createThunkMiddleware;
export const withExtraArgument = createThunkMiddleware;