Match-id-0c7552eff73b3193eb58fd25a2dfe13e09f93e41

This commit is contained in:
* 2022-08-31 16:41:25 +08:00
parent cb87ec505a
commit b20293076c
8 changed files with 62 additions and 188 deletions

View File

@ -33,6 +33,7 @@ import { runAsyncEffects } from './src/renderer/submit/HookEffectHandler';
import { createStore, useStore, clearStore } from './src/horizonx/store/StoreHandler';
import * as reduxAdapter from './src/horizonx/adapters/redux';
import { watch } from './src/horizonx/proxy/watch';
// act用于测试作用是如果fun触发了刷新包含了异步刷新可以保证在act后面的代码是在刷新完成后才执行。
const act = fun => {
@ -85,6 +86,7 @@ const Horizon = {
useStore,
clearStore,
reduxAdapter,
watch
};
export const version = __VERSION__;
@ -125,6 +127,7 @@ export {
useStore,
clearStore,
reduxAdapter,
watch
};
export default Horizon;

View File

@ -33,6 +33,8 @@ export class Observer implements IObserver {
listeners:(()=>void)[] = [];
watchers={} as {[key:string]:((key:string, oldValue:any, newValue:any)=>void)[]}
useProp(key: string | symbol): void {
const processingVNode = getProcessingVNode();
if (processingVNode === null || !processingVNode.observers) {

View File

@ -12,6 +12,20 @@ export function createArrayProxy(rawObj: any[]): any[] {
}
function get(rawObj: any[], key: string, receiver: any) {
if (key === 'watch'){
const observer = getObserver(rawObj);
return (prop:any, handler:(key:string, oldValue:any, newValue:any)=>void)=>{
if(!observer.watchers[prop]){
observer.watchers[prop]=[] as ((key:string, oldValue:any, newValue:any)=>void)[];
}
observer.watchers[prop].push(handler);
return ()=>{
observer.watchers[prop]=observer.watchers[prop].filter(cb=>cb!==handler);
}
}
}
if (isValidIntegerKey(key) || key === 'length') {
return objectGet(rawObj, key, receiver);
}
@ -29,6 +43,12 @@ function set(rawObj: any[], key: string, value: any, receiver: any) {
const observer = getObserver(rawObj);
if (!isSame(newValue, oldValue)) {
if(observer.watchers?.[key]){
observer.watchers[key].forEach(cb => {
cb(key, oldValue, newValue);
});
}
observer.setProp(key);
}

View File

@ -34,6 +34,18 @@ function get(rawObj: { size: number }, key: any, receiver: any): any {
} else if (Object.prototype.hasOwnProperty.call(handler, key)) {
const value = Reflect.get(handler, key, receiver);
return value.bind(null, rawObj);
} else if (key === 'watch'){
const observer = getObserver(rawObj);
return (prop:any, handler:(key:string, oldValue:any, newValue:any)=>void)=>{
if(!observer.watchers[prop]){
observer.watchers[prop]=[] as ((key:string, oldValue:any, newValue:any)=>void)[];
}
observer.watchers[prop].push(handler);
return ()=>{
observer.watchers[prop]=observer.watchers[prop].filter(cb=>cb!==handler);
}
}
}
return Reflect.get(rawObj, key, receiver);
@ -67,6 +79,12 @@ function set(
}
if (valChange) {
if(observer.watchers?.[key]){
observer.watchers[key].forEach(cb => {
cb(key, oldValue, newValue);
});
}
observer.setProp(key);
}

View File

@ -19,6 +19,18 @@ export function get(rawObj: object, key: string | symbol, receiver: any, singleL
const observer = getObserver(rawObj);
if (key === 'watch'){
return (prop, handler:(key:string, oldValue:any, newValue:any)=>void)=>{
if(!observer.watchers[prop]){
observer.watchers[prop]=[] as ((key:string, oldValue:any, newValue:any)=>void)[];
}
observer.watchers[prop].push(handler);
return ()=>{
observer.watchers[prop]=observer.watchers[prop].filter(cb=>cb!==handler);
}
}
}
if (key === 'addListener') {
return observer.addListener.bind(observer);
}
@ -54,6 +66,11 @@ export function set(rawObj: object, key: string, value: any, receiver: any): boo
const ret = Reflect.set(rawObj, key, newValue, receiver);
if (!isSame(newValue, oldValue)) {
if(observer.watchers?.[key]){
observer.watchers[key].forEach(cb => {
cb(key, oldValue, newValue);
});
}
observer.setProp(key);
}

View File

@ -57,7 +57,7 @@ export function captureVNode(processing: VNode): VNode | null {
setProcessingVNode(processing);
clearVNodeObservers(processing);
if(processing.observers) clearVNodeObservers(processing);
const child = component.captureRender(processing, shouldUpdate);
setProcessingVNode(null);

View File

@ -1,186 +0,0 @@
//@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 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);
Horizon.act(() => {
triggerClickEvent(container, BUTTON_ID);
triggerClickEvent(container, BUTTON_ID);
triggerClickEvent(container, BUTTON_ID2);
triggerClickEvent(container, BUTTON_ID2);
});
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('9');
});
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);
Horizon.act(() => {
triggerClickEvent(container, BUTTON_ID);
triggerClickEvent(container, BUTTON_ID);
triggerClickEvent(container, BUTTON_ID2);
triggerClickEvent(container, BUTTON_ID2);
});
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('9');
});
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);
Horizon.act(() => {
triggerClickEvent(container, BUTTON_ID);
triggerClickEvent(container, BUTTON_ID);
triggerClickEvent(container, BUTTON_ID2);
triggerClickEvent(container, BUTTON_ID2);
});
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('9');
});
});