feat: Every child node subscribe its parent.

This commit is contained in:
haiqin 2024-01-25 09:36:46 +08:00
parent 04b12cc423
commit ffd2bf2fc5
5 changed files with 79 additions and 23 deletions

View File

@ -13,6 +13,8 @@
* See the Mulan PSL v2 for more details.
*/
import { printNode } from './utils/printNode';
let runningRNode: RNode<any> | undefined = undefined; // 当前正执行的RNode
let calledGets: RNode<any>[] | null = null;
let sameGetsIndex = 0; // 记录前后两次运行RNode时调用get顺序没有变化的节点
@ -53,7 +55,7 @@ export class RNode<T = any> {
private sources: RNode[] | null = null; // 使用谁
private state: State;
private isEffect = false;
isEffect = false;
cleanups: ((oldValue: T) => void)[] = [];
@ -102,6 +104,9 @@ export class RNode<T = any> {
} else {
calledGets.push(this);
}
if (process.env.NODE_ENV === 'debug') {
console.log('Track: ', printNode(this));
}
}
}
}
@ -182,6 +187,10 @@ export class RNode<T = any> {
this.cleanups = [];
}
if (process.env.NODE_ENV === 'debug') {
console.log(`Running: ${printNode(this)}`);
}
// 执行 reactive 函数
this.execute();
@ -207,6 +216,10 @@ export class RNode<T = any> {
} else {
source.observers.push(this);
}
if (process.env.NODE_ENV === 'debug') {
console.log(`Bind: ${printNode(this)} -> ${printNode(source)}`);
}
}
} else if (this.sources && sameGetsIndex < this.sources.length) {
// remove all old sources' .observers links to us
@ -311,3 +324,4 @@ export function untrack(fn) {
runningRNode = preRContext;
}
}

View File

@ -55,17 +55,9 @@ export function getOrCreateChildRNode(node: RProxyNode<any>, key: string | symbo
let child = node.children?.get(key);
if (!child) {
// child = new RProxyNode(null, {
// parent: node,
// key: key,
// root: node.root,
// });
child = new RProxyNode(
() => {
const rootRNode = getRootRNode(node);
// 依赖根
rootRNode.get();
node.get();
return getRNodeVal(node)[key];
},
@ -78,7 +70,5 @@ export function getOrCreateChildRNode(node: RProxyNode<any>, key: string | symbo
);
}
child.track();
return child;
}

View File

@ -14,10 +14,10 @@
*/
import { createProxy } from './proxy/RProxyHandler';
import {getRNodeVal, getRootRNode, setRNodeVal} from './RNodeAccessor';
import { getRNodeVal, getRootRNode, setRNodeVal } from './RNodeAccessor';
import { preciseCompare } from './comparison/InDepthComparison';
import { isObject } from './Utils';
import {RNode, Root, runEffects} from "./RNode";
import { RNode, Root, runEffects } from './RNode';
export interface RNodeOptions {
root?: Root<any> | null;
@ -83,13 +83,6 @@ export class RProxyNode<T = any> extends RNode<T> {
}
}
execute() {
// 执行 reactive 函数
if (this.isComputed) {
setRNodeVal(this, this.fn!());
}
}
setByArrayModified(value: T) {
const prevValue = this.getValue();
@ -104,7 +97,7 @@ export class RProxyNode<T = any> extends RNode<T> {
}
getValue() {
return getRNodeVal(this);
return this._value;
}
setValue(value: T) {

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
import { RNode } from '../RNode';
export function printNode(signal: RNode) {
let name: string;
if (signal.fn) {
if (signal.isEffect) {
name = `Effect${signal.fn.name ?? ''}`;
} else {
name = `Computation(${(signal as unknown as any).key.toString()})`;
}
} else {
name = 'Signal';
}
return name;
}

View File

@ -63,6 +63,35 @@ describe('test reactive', () => {
expect(yCalledTimes).toBe(2);
});
it('overwrite object reactive should keep reactive', () => {
const pos = reactive({ x: 0, y: 0 });
const xWatch = jest.fn();
watch(() => {
xWatch(pos.x.get());
});
const yWatch = jest.fn();
watch(() => {
yWatch(pos.y.get());
});
expect(xWatch).toBeCalledTimes(1);
expect(yWatch).toBeCalledTimes(1);
pos.set({x: 1, y: 1});
expect(xWatch).toBeCalledTimes(2);
expect(yWatch).toBeCalledTimes(2);
pos.set({x: 2, y: 1});
expect(xWatch).toBeCalledTimes(3);
expect(yWatch).toBeCalledTimes(2);
pos.set({x: 2, y: 2});
expect(xWatch).toBeCalledTimes(3);
expect(yWatch).toBeCalledTimes(3);
});
it('reactive is a obj', () => {
const rObj = reactive({ count: 1 });
@ -110,7 +139,6 @@ describe('test reactive', () => {
a.set(2);
expect(c.a.read()).toBe(2);
});
it('reactive is a array, watch', () => {