Match-id-0a9b956d94031f8d8f03fb90913325154133063d
This commit is contained in:
parent
b20293076c
commit
fbf7e8370b
|
@ -32,34 +32,34 @@ export type ReduxMiddleware = (
|
|||
|
||||
type Reducer = (state: any, action: ReduxAction) => any;
|
||||
|
||||
function mergeData(state,data){
|
||||
console.log('merging data',{state,data});
|
||||
if(!data){
|
||||
function mergeData(state, data) {
|
||||
console.log('merging data', { state, data });
|
||||
if (!data) {
|
||||
console.log('!data');
|
||||
state.stateWrapper=data;
|
||||
state.stateWrapper = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(Array.isArray(data) && Array.isArray(state?.stateWrapper)){
|
||||
if (Array.isArray(data) && Array.isArray(state?.stateWrapper)) {
|
||||
console.log('data is array');
|
||||
state.stateWrapper.length = data.length;
|
||||
data.forEach((item,idx) => {
|
||||
if(item!=state.stateWrapper[idx]){
|
||||
state.stateWrapper[idx]=item;
|
||||
data.forEach((item, idx) => {
|
||||
if (item != state.stateWrapper[idx]) {
|
||||
state.stateWrapper[idx] = item;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof data === 'object' && typeof state?.stateWrapper === 'object'){
|
||||
if (typeof data === 'object' && typeof state?.stateWrapper === 'object') {
|
||||
console.log('data is object');
|
||||
Object.keys(state.stateWrapper).forEach(key => {
|
||||
if(!data.hasOwnProperty(key)) delete state.stateWrapper[key];
|
||||
})
|
||||
if (!data.hasOwnProperty(key)) delete state.stateWrapper[key];
|
||||
});
|
||||
|
||||
Object.entries(data).forEach(([key,value])=>{
|
||||
if(state.stateWrapper[key]!==value){
|
||||
state.stateWrapper[key]=value;
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
if (state.stateWrapper[key] !== value) {
|
||||
state.stateWrapper[key] = value;
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
@ -100,7 +100,7 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?):
|
|||
|
||||
const result = {
|
||||
reducer,
|
||||
getState: function() {
|
||||
getState: function () {
|
||||
return store.$s.stateWrapper;
|
||||
},
|
||||
subscribe: listener => {
|
||||
|
@ -169,7 +169,7 @@ export function bindActionCreators(actionCreators: ActionCreators, dispatch: Dis
|
|||
return boundActionCreators;
|
||||
}
|
||||
|
||||
export function compose(middlewares: ReduxMiddleware[]) {
|
||||
export function compose(...middlewares: ReduxMiddleware[]) {
|
||||
return (store: ReduxStoreHandler, extraArgument: any) => {
|
||||
let val;
|
||||
middlewares.reverse().forEach((middleware: ReduxMiddleware, index) => {
|
||||
|
|
|
@ -4,7 +4,8 @@ import { createContext } from '../../renderer/components/context/CreateContext';
|
|||
import { createElement } from '../../external/JSXElement';
|
||||
import { BoundActionCreator } from './redux';
|
||||
import { ReduxAction } from './redux';
|
||||
import { ReduxStoreHandler } from '../store/StoreHandler'
|
||||
import { ReduxStoreHandler } from '../store/StoreHandler';
|
||||
import { VNode } from '../../renderer/Types';
|
||||
|
||||
const DefaultContext = createContext(null);
|
||||
type Context = typeof DefaultContext;
|
||||
|
@ -22,15 +23,15 @@ export function Provider({
|
|||
return createElement(Context.Provider, { value: store }, children);
|
||||
}
|
||||
|
||||
export function createStoreHook(context: Context) {
|
||||
export function createStoreHook(context: Context): () => ReduxStoreHandler {
|
||||
return () => {
|
||||
return useContext(context);
|
||||
return useContext(context) as unknown as ReduxStoreHandler;
|
||||
};
|
||||
}
|
||||
|
||||
export function createSelectorHook(context: Context): (selector?: (any) => any) => any {
|
||||
const store = (createStoreHook(context)() as unknown) as ReduxStoreHandler;
|
||||
return function(selector = state => state) {
|
||||
const store = createStoreHook(context)() as unknown as ReduxStoreHandler;
|
||||
return function (selector = state => state) {
|
||||
const [b, fr] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -44,9 +45,9 @@ export function createSelectorHook(context: Context): (selector?: (any) => any)
|
|||
};
|
||||
}
|
||||
|
||||
export function createDispatchHook(context: Context): ()=>BoundActionCreator {
|
||||
const store = (createStoreHook(context)() as unknown) as ReduxStoreHandler;
|
||||
return function() {
|
||||
export function createDispatchHook(context: Context): () => BoundActionCreator {
|
||||
const store = createStoreHook(context)() as unknown as ReduxStoreHandler;
|
||||
return function () {
|
||||
return action => {
|
||||
store.dispatch(action);
|
||||
};
|
||||
|
@ -84,28 +85,46 @@ export const useStore = () => {
|
|||
// areMergedPropsEqual: shallowCompare
|
||||
// };
|
||||
|
||||
export function connect(
|
||||
mapStateToProps?: (state: any, ownProps: { [key: string]: any }) => Object,
|
||||
mapDispatchToProps?:
|
||||
| { [key: string]: (...args: any[]) => ReduxAction }
|
||||
| ((dispatch: (action: ReduxAction) => any, ownProps?: Object) => Object),
|
||||
mergeProps?: (stateProps: Object, dispatchProps: Object, ownProps: Object) => Object,
|
||||
type MapStateToPropsP<StateProps, OwnProps> = (state: any, ownProps: OwnProps) => StateProps;
|
||||
type MapDispatchToPropsP<DispatchProps, OwnProps> =
|
||||
| { [key: string]: (...args: any[]) => ReduxAction }
|
||||
| ((dispatch: (action: ReduxAction) => any, ownProps: OwnProps) => DispatchProps);
|
||||
type MergePropsP<StateProps, DispatchProps, OwnProps, MergedProps> = (
|
||||
stateProps: StateProps,
|
||||
dispatchProps: DispatchProps,
|
||||
ownProps: OwnProps
|
||||
) => MergedProps;
|
||||
|
||||
type WrappedComponent<OwnProps> = (props: OwnProps) => ReturnType<typeof createElement>;
|
||||
type OriginalComponent<MergedProps> = (props: MergedProps) => ReturnType<typeof createElement>;
|
||||
type Connector<OwnProps, MergedProps> = (Component: OriginalComponent<MergedProps>) => WrappedComponent<OwnProps>;
|
||||
|
||||
export function connect<StateProps, DispatchProps, OwnProps, MergedProps>(
|
||||
mapStateToProps: MapStateToPropsP<StateProps, OwnProps> = () => ({} as StateProps),
|
||||
mapDispatchToProps: MapDispatchToPropsP<DispatchProps, OwnProps> = () => ({} as DispatchProps),
|
||||
mergeProps: MergePropsP<StateProps, DispatchProps, OwnProps, MergedProps> = (
|
||||
stateProps,
|
||||
dispatchProps,
|
||||
ownProps
|
||||
): MergedProps => ({ ...stateProps, ...dispatchProps, ...ownProps } as MergedProps),
|
||||
options?: {
|
||||
areStatesEqual?: (oldState: any, newState: any) => boolean;
|
||||
context?: any; // TODO: type this
|
||||
context?: Context;
|
||||
}
|
||||
) {
|
||||
): Connector<OwnProps, MergedProps> {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
return Component => {
|
||||
//this component should bear the type returned from mapping functions
|
||||
return (Component: OriginalComponent<MergedProps>): WrappedComponent<OwnProps> => {
|
||||
const useStore = createStoreHook(options?.context || DefaultContext);
|
||||
|
||||
function Wrapper(props) {
|
||||
//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();
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = store.subscribe(() => forceReload(!f));
|
||||
|
@ -119,36 +138,34 @@ export function connect(
|
|||
mappedState: {},
|
||||
}) as {
|
||||
current: {
|
||||
state: {};
|
||||
mappedState: {};
|
||||
state: { [key: string]: any };
|
||||
mappedState: StateProps;
|
||||
};
|
||||
};
|
||||
|
||||
let mappedState;
|
||||
let mappedState: StateProps;
|
||||
if (options?.areStatesEqual) {
|
||||
if (options.areStatesEqual(previous.current.state, store.getState())) {
|
||||
mappedState = previous.current.mappedState;
|
||||
mappedState = previous.current.mappedState as StateProps;
|
||||
} else {
|
||||
mappedState = mapStateToProps ? mapStateToProps(store.getState(), props) : {};
|
||||
mappedState = mapStateToProps ? mapStateToProps(store.getState(), props) : ({} as StateProps);
|
||||
previous.current.mappedState = mappedState;
|
||||
}
|
||||
} else {
|
||||
mappedState = mapStateToProps ? mapStateToProps(store.getState(), props) : {};
|
||||
mappedState = mapStateToProps ? mapStateToProps(store.getState(), props) : ({} as StateProps);
|
||||
previous.current.mappedState = mappedState;
|
||||
}
|
||||
let mappedDispatch: { dispatch?: (action) => void } = {};
|
||||
let mappedDispatch: DispatchProps = {} as DispatchProps;
|
||||
if (mapDispatchToProps) {
|
||||
if (typeof mapDispatchToProps === 'object') {
|
||||
Object.entries(mapDispatchToProps).forEach(([key, value]) => {
|
||||
mappedDispatch[key] = (...args) => {
|
||||
mappedDispatch[key] = (...args: ReduxAction[]) => {
|
||||
store.dispatch(value(...args));
|
||||
};
|
||||
});
|
||||
} else {
|
||||
mappedDispatch = mapDispatchToProps(store.dispatch, props);
|
||||
}
|
||||
} else {
|
||||
mappedDispatch.dispatch = store.dispatch;
|
||||
}
|
||||
const mergedProps = (
|
||||
mergeProps ||
|
||||
|
@ -161,7 +178,7 @@ export function connect(
|
|||
|
||||
const node = createElement(Component, mergedProps);
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
return Wrapper;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
export function watch(stateVariable:any,listener:(stateVariable:any)=>void){
|
||||
listener = listener.bind(null,stateVariable);
|
||||
stateVariable.addListener(listener);
|
||||
|
||||
return ()=>{
|
||||
stateVariable.removeListener(listener);
|
||||
}
|
||||
}
|
|
@ -35,9 +35,7 @@ type StoreHandler<S extends object, A extends UserActions<S>, C extends UserComp
|
|||
$queue: QueuedStoreActions<S, A>;
|
||||
$a: StoreActions<S, A>;
|
||||
$c: UserComputedValues<S>;
|
||||
} & { [K in keyof S]: S[K] } &
|
||||
{ [K in keyof A]: Action<A[K], S> } &
|
||||
{ [K in keyof C]: ReturnType<C[K]> };
|
||||
} & { [K in keyof S]: S[K] } & { [K in keyof A]: Action<A[K], S> } & { [K in keyof C]: ReturnType<C[K]> };
|
||||
|
||||
type PlannedAction<S extends object, F extends ActionFunction<S>> = {
|
||||
action: string;
|
||||
|
@ -76,6 +74,7 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
|||
//create a local shalow copy to ensure consistency (if user would change the config object after store creation)
|
||||
config = {
|
||||
id: config.id,
|
||||
/* @ts-ignore*/
|
||||
options: config.options,
|
||||
state: config.state,
|
||||
actions: config.actions ? { ...config.actions } : undefined,
|
||||
|
@ -87,6 +86,7 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
|||
throw new Error('store obj must be pure object');
|
||||
}
|
||||
|
||||
/* @ts-ignore*/
|
||||
const proxyObj = createProxy(config.state, !config.options?.reduxAdapter);
|
||||
|
||||
proxyObj.$pending = false;
|
||||
|
@ -103,7 +103,7 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
|||
const $a: Partial<StoreActions<S, A>> = {};
|
||||
const $queue: Partial<StoreActions<S, A>> = {};
|
||||
const $c: Partial<ComputedValues<S, C>> = {};
|
||||
const handler = ({
|
||||
const handler = {
|
||||
$subscribe,
|
||||
$unsubscribe,
|
||||
$a: $a as StoreActions<S, A>,
|
||||
|
@ -111,7 +111,7 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
|||
$c: $c as ComputedValues<S, C>,
|
||||
$config: config,
|
||||
$queue: $queue as QueuedStoreActions<S, A>,
|
||||
} as unknown) as StoreHandler<S, A, C>;
|
||||
} as unknown as StoreHandler<S, A, C>;
|
||||
|
||||
function tryNextAction() {
|
||||
if (!plannedActions.length) {
|
||||
|
@ -193,6 +193,9 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
|||
Object.keys(config.state).forEach(key => {
|
||||
Object.defineProperty(handler, key, {
|
||||
get: () => proxyObj[key],
|
||||
set: value => {
|
||||
proxyObj[key] = value;
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -205,7 +208,7 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
|||
}
|
||||
|
||||
export function clearVNodeObservers(vNode) {
|
||||
if(!vNode.observers) return;
|
||||
if (!vNode.observers) return;
|
||||
vNode.observers.forEach(observer => {
|
||||
observer.clearByVNode(vNode);
|
||||
});
|
||||
|
@ -220,14 +223,14 @@ function hookStore() {
|
|||
if (!processingVNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!processingVNode.observers) {
|
||||
processingVNode.observers = new Set<Observer>();
|
||||
}
|
||||
|
||||
if (processingVNode.tag === FunctionComponent) {
|
||||
// from FunctionComponent
|
||||
const vNodeRef = (useRef(null) as unknown) as { current: VNode };
|
||||
const vNodeRef = useRef(null) as unknown as { current: VNode };
|
||||
vNodeRef.current = processingVNode;
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -239,7 +242,7 @@ function hookStore() {
|
|||
} else if (processingVNode.tag === ClassComponent) {
|
||||
// from ClassComponent
|
||||
if (!processingVNode.classComponentWillUnmount) {
|
||||
processingVNode.classComponentWillUnmount = function(vNode) {
|
||||
processingVNode.classComponentWillUnmount = function (vNode) {
|
||||
clearVNodeObservers(vNode);
|
||||
vNode.observers = null;
|
||||
};
|
||||
|
|
|
@ -34,11 +34,11 @@
|
|||
"@babel/plugin-transform-object-super": "7.16.7",
|
||||
"@babel/plugin-transform-parameters": "7.16.7",
|
||||
"@babel/plugin-transform-react-jsx": "7.16.7",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.16.7",
|
||||
"@babel/plugin-transform-runtime": "7.16.7",
|
||||
"@babel/plugin-transform-shorthand-properties": "7.16.7",
|
||||
"@babel/plugin-transform-spread": "7.16.7",
|
||||
"@babel/plugin-transform-template-literals": "7.16.7",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.16.7",
|
||||
"@babel/preset-env": "7.16.7",
|
||||
"@babel/preset-typescript": "7.16.7",
|
||||
"@rollup/plugin-babel": "^5.3.1",
|
||||
|
@ -66,5 +66,8 @@
|
|||
"engines": {
|
||||
"node": ">=10.x",
|
||||
"npm": ">=7.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"ejs": "^3.1.8"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,34 @@ describe('Basic store manipulation', () => {
|
|||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1');
|
||||
});
|
||||
|
||||
it('Should use direct setters', () => {
|
||||
function App() {
|
||||
const logStore = useLogStore();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
id={BUTTON_ID}
|
||||
onClick={() => {
|
||||
logStore.logs = ['q'];
|
||||
}}
|
||||
>
|
||||
add
|
||||
</button>
|
||||
<p id={RESULT_ID}>{logStore.logs[0]}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Horizon.render(<App />, container);
|
||||
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, BUTTON_ID);
|
||||
});
|
||||
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('q');
|
||||
});
|
||||
|
||||
it('Should use actions and update components', () => {
|
||||
function App() {
|
||||
const logStore = useLogStore();
|
||||
|
@ -74,7 +102,7 @@ describe('Basic store manipulation', () => {
|
|||
increment: state => {
|
||||
state.count++;
|
||||
},
|
||||
doublePlusOne: function(state) {
|
||||
doublePlusOne: function (state) {
|
||||
state.count = state.count * 2;
|
||||
this.increment();
|
||||
},
|
||||
|
@ -115,20 +143,20 @@ describe('Basic store manipulation', () => {
|
|||
count: 2,
|
||||
},
|
||||
actions: {
|
||||
doublePlusOne: function(state) {
|
||||
doublePlusOne: function (state) {
|
||||
state.count = this.double + 1;
|
||||
},
|
||||
},
|
||||
computed:{
|
||||
double: (state) => {
|
||||
return state.count*2
|
||||
}
|
||||
}
|
||||
computed: {
|
||||
double: state => {
|
||||
return state.count * 2;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
function App() {
|
||||
const incrementStore = useIncrementStore();
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
|
@ -143,13 +171,13 @@ describe('Basic store manipulation', () => {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Horizon.render(<App />, container);
|
||||
|
||||
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, BUTTON_ID);
|
||||
});
|
||||
|
||||
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('5');
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
import { createStore } from "@cloudsop/horizon/src/horizonx/store/StoreHandler";
|
||||
import { watch } from "@cloudsop/horizon/src/horizonx/proxy/watch";
|
||||
|
||||
describe("watch",()=>{
|
||||
it('shouhld watch promitive state variable', async()=>{
|
||||
const useStore = createStore({
|
||||
state:{
|
||||
variable:'x'
|
||||
},
|
||||
actions:{
|
||||
change:(state)=>state.variable = "a"
|
||||
}
|
||||
});
|
||||
|
||||
const store = useStore();
|
||||
let counter = 0;
|
||||
|
||||
watch(store.$s,(state)=>{
|
||||
counter++;
|
||||
expect(state.variable).toBe('a');
|
||||
})
|
||||
|
||||
store.change();
|
||||
|
||||
expect(counter).toBe(1);
|
||||
});
|
||||
it('shouhld watch object variable', async()=>{
|
||||
const useStore = createStore({
|
||||
state:{
|
||||
variable:'x'
|
||||
},
|
||||
actions:{
|
||||
change:(state)=>state.variable = "a"
|
||||
}
|
||||
});
|
||||
|
||||
const store = useStore();
|
||||
let counter = 0;
|
||||
|
||||
store.$s.watch('variable',()=>{
|
||||
counter++;
|
||||
})
|
||||
|
||||
store.change();
|
||||
|
||||
expect(counter).toBe(1);
|
||||
});
|
||||
|
||||
it('shouhld watch array item', async()=>{
|
||||
const useStore = createStore({
|
||||
state:{
|
||||
arr:['x']
|
||||
},
|
||||
actions:{
|
||||
change:(state)=>state.arr[0]='a'
|
||||
}
|
||||
});
|
||||
|
||||
const store = useStore();
|
||||
let counter = 0;
|
||||
|
||||
store.arr.watch('0',()=>{
|
||||
counter++;
|
||||
})
|
||||
|
||||
store.change();
|
||||
|
||||
expect(counter).toBe(1);
|
||||
});
|
||||
|
||||
it('shouhld watch collection item', async()=>{
|
||||
const useStore = createStore({
|
||||
state:{
|
||||
collection:new Map([
|
||||
['a', 'a'],
|
||||
])
|
||||
},
|
||||
actions:{
|
||||
change:(state)=>state.collection.set('a','x')
|
||||
}
|
||||
});
|
||||
|
||||
const store = useStore();
|
||||
let counter = 0;
|
||||
|
||||
store.collection.watch('a',()=>{
|
||||
counter++;
|
||||
})
|
||||
|
||||
store.change();
|
||||
|
||||
expect(counter).toBe(1);
|
||||
});
|
||||
|
||||
it('should watch multiple variables independedntly', async()=>{
|
||||
const useStore = createStore({
|
||||
state:{
|
||||
bool1:true,
|
||||
bool2:false
|
||||
},
|
||||
actions:{
|
||||
toggle1:state=>state.bool1=!state.bool1,
|
||||
toggle2:state=>state.bool2=!state.bool2
|
||||
}
|
||||
});
|
||||
|
||||
let counter1=0;
|
||||
let counterAll=0;
|
||||
const store = useStore();
|
||||
|
||||
watch(store.$s,()=>{
|
||||
counterAll++;
|
||||
})
|
||||
|
||||
store.$s.watch('bool1',()=>{
|
||||
counter1++;
|
||||
});
|
||||
|
||||
store.toggle1();
|
||||
store.toggle1();
|
||||
|
||||
store.toggle2();
|
||||
|
||||
store.toggle1();
|
||||
|
||||
store.toggle2();
|
||||
store.toggle2();
|
||||
|
||||
expect(counter1).toBe(3);
|
||||
expect(counterAll).toBe(6);
|
||||
})
|
||||
})
|
|
@ -293,4 +293,4 @@ describe('Redux/React binding adapter', () => {
|
|||
expect(getE(BUTTON).innerHTML).toBe('1');
|
||||
expect(getE(BUTTON2).innerHTML).toBe('true');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
import { createElement } from '../../../../libs/horizon/src/external/JSXElement';
|
||||
import { createDomTextVNode } from '../../../../libs/horizon/src/renderer/vnode/VNodeCreator';
|
||||
import { createStore } from '../../../../libs/horizon/src/horizonx/adapters/redux';
|
||||
import { connect } from '../../../../libs/horizon/src/horizonx/adapters/reduxReact';
|
||||
|
||||
createStore((state: number = 0, action): number => {
|
||||
if (action.type === 'add') return state + 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
type WrappedButtonProps = { add: () => void; count: number; text: string };
|
||||
|
||||
function Button(props: WrappedButtonProps) {
|
||||
const { add, count, text } = props;
|
||||
return createElement(
|
||||
'button',
|
||||
{
|
||||
onClick: add,
|
||||
},
|
||||
createDomTextVNode(text),
|
||||
createDomTextVNode(': '),
|
||||
createDomTextVNode(count)
|
||||
);
|
||||
}
|
||||
|
||||
const connector = connect(
|
||||
state => ({ count: state }),
|
||||
dispatch => ({
|
||||
add: (): void => {
|
||||
dispatch({ type: 'add' });
|
||||
},
|
||||
}),
|
||||
(stateProps, dispatchProps, ownProps: { text: string }) => ({
|
||||
add: dispatchProps.add,
|
||||
count: stateProps.count,
|
||||
text: ownProps.text,
|
||||
})
|
||||
);
|
||||
|
||||
const ConnectedButton = connector(Button);
|
||||
|
||||
function App() {
|
||||
return createElement('div', {}, createElement(ConnectedButton, { text: 'click' }));
|
||||
}
|
||||
|
||||
export default App;
|
|
@ -0,0 +1,199 @@
|
|||
//@ts-ignore
|
||||
import Horizon, { createStore } from '@cloudsop/horizon/index.ts';
|
||||
import { triggerClickEvent } from '../../jest/commonComponents';
|
||||
import { describe, beforeEach, afterEach, it, expect } from '@jest/globals';
|
||||
|
||||
const { unmountComponentAtNode } = Horizon;
|
||||
|
||||
const useStore1 = createStore({
|
||||
state:{ counter:1 },
|
||||
actions:{
|
||||
add:(state)=>state.counter++,
|
||||
reset: (state)=>state.counter=1
|
||||
}
|
||||
})
|
||||
|
||||
const useStore2 = createStore({
|
||||
state:{ counter2:1 },
|
||||
actions:{
|
||||
add2:(state)=>state.counter2++,
|
||||
reset: (state)=>state.counter2=1
|
||||
}
|
||||
})
|
||||
|
||||
describe('Using multiple stores', () => {
|
||||
let container: HTMLElement | null = null;
|
||||
|
||||
const BUTTON_ID = 'btn';
|
||||
const BUTTON_ID2 = 'btn2';
|
||||
const RESULT_ID = 'result';
|
||||
|
||||
beforeEach(() => {
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
useStore1().reset();
|
||||
useStore2().reset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
unmountComponentAtNode(container);
|
||||
container?.remove();
|
||||
container = null;
|
||||
});
|
||||
|
||||
it('Should use multiple stores in class component', () => {
|
||||
class App extends Horizon.Component{
|
||||
render(){
|
||||
const {counter,add} = useStore1();
|
||||
const {counter2, add2} = useStore2();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
id={BUTTON_ID}
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
>
|
||||
add
|
||||
</button>
|
||||
<button
|
||||
id={BUTTON_ID2}
|
||||
onClick={() => {
|
||||
add2();
|
||||
}}
|
||||
>
|
||||
add
|
||||
</button>
|
||||
<p id={RESULT_ID}>{counter} {counter2}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Horizon.render(<App />, container);
|
||||
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1 1');
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, BUTTON_ID);
|
||||
|
||||
});
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 1');
|
||||
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, BUTTON_ID2);
|
||||
|
||||
});
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 2');
|
||||
|
||||
});
|
||||
|
||||
it('Should use use stores in cycles and multiple methods', () => {
|
||||
interface App {
|
||||
store:any,
|
||||
store2:any
|
||||
}
|
||||
class App extends Horizon.Component{
|
||||
constructor(){
|
||||
super();
|
||||
this.store = useStore1();
|
||||
this.store2 = useStore2()
|
||||
}
|
||||
|
||||
render(){
|
||||
const {counter,add} = useStore1();
|
||||
const store2 = useStore2();
|
||||
const {counter2, add2} = store2;
|
||||
|
||||
for(let i=0; i<100; i++){
|
||||
const {counter,add} = useStore1();
|
||||
const store2 = useStore2();
|
||||
const {counter2, add2} = store2;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
id={BUTTON_ID}
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
>
|
||||
add
|
||||
</button>
|
||||
<button
|
||||
id={BUTTON_ID2}
|
||||
onClick={() => {
|
||||
this.store2.add2();
|
||||
}}
|
||||
>
|
||||
add
|
||||
</button>
|
||||
<p id={RESULT_ID}>{counter} {counter2}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Horizon.render(<App />, container);
|
||||
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1 1');
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, BUTTON_ID);
|
||||
|
||||
});
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 1');
|
||||
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, BUTTON_ID2);
|
||||
|
||||
});
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 2');
|
||||
|
||||
});
|
||||
|
||||
it('Should use multiple stores in function component', () => {
|
||||
function App() {
|
||||
const {counter,add} = useStore1();
|
||||
const store2 = useStore2();
|
||||
const {counter2, add2} = store2;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
id={BUTTON_ID}
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
>
|
||||
add
|
||||
</button>
|
||||
<button
|
||||
id={BUTTON_ID2}
|
||||
onClick={() => {
|
||||
add2();
|
||||
}}
|
||||
>
|
||||
add
|
||||
</button>
|
||||
<p id={RESULT_ID}>{counter} {counter2}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Horizon.render(<App />, container);
|
||||
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('1 1');
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, BUTTON_ID);
|
||||
|
||||
});
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 1');
|
||||
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, BUTTON_ID2);
|
||||
|
||||
});
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('2 2');
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue