!120 feat: Every child node subscribe its parent.
Merge pull request !120 from Hoikan/reactive
This commit is contained in:
commit
240ba6892d
|
@ -13,6 +13,8 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { printNode } from './utils/printNode';
|
||||||
|
|
||||||
let runningRNode: RNode<any> | undefined = undefined; // 当前正执行的RNode
|
let runningRNode: RNode<any> | undefined = undefined; // 当前正执行的RNode
|
||||||
let calledGets: RNode<any>[] | null = null;
|
let calledGets: RNode<any>[] | null = null;
|
||||||
let sameGetsIndex = 0; // 记录前后两次运行RNode时,调用get顺序没有变化的节点
|
let sameGetsIndex = 0; // 记录前后两次运行RNode时,调用get顺序没有变化的节点
|
||||||
|
@ -53,7 +55,7 @@ export class RNode<T = any> {
|
||||||
private sources: RNode[] | null = null; // 使用谁
|
private sources: RNode[] | null = null; // 使用谁
|
||||||
|
|
||||||
private state: State;
|
private state: State;
|
||||||
private isEffect = false;
|
isEffect = false;
|
||||||
|
|
||||||
|
|
||||||
cleanups: ((oldValue: T) => void)[] = [];
|
cleanups: ((oldValue: T) => void)[] = [];
|
||||||
|
@ -102,6 +104,9 @@ export class RNode<T = any> {
|
||||||
} else {
|
} else {
|
||||||
calledGets.push(this);
|
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 = [];
|
this.cleanups = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'debug') {
|
||||||
|
console.log(`Running: ${printNode(this)}`);
|
||||||
|
}
|
||||||
|
|
||||||
// 执行 reactive 函数
|
// 执行 reactive 函数
|
||||||
this.execute();
|
this.execute();
|
||||||
|
|
||||||
|
@ -207,6 +216,10 @@ export class RNode<T = any> {
|
||||||
} else {
|
} else {
|
||||||
source.observers.push(this);
|
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) {
|
} else if (this.sources && sameGetsIndex < this.sources.length) {
|
||||||
// remove all old sources' .observers links to us
|
// remove all old sources' .observers links to us
|
||||||
|
@ -311,3 +324,4 @@ export function untrack(fn) {
|
||||||
runningRNode = preRContext;
|
runningRNode = preRContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,17 +55,9 @@ export function getOrCreateChildRNode(node: RProxyNode<any>, key: string | symbo
|
||||||
let child = node.children?.get(key);
|
let child = node.children?.get(key);
|
||||||
|
|
||||||
if (!child) {
|
if (!child) {
|
||||||
// child = new RProxyNode(null, {
|
|
||||||
// parent: node,
|
|
||||||
// key: key,
|
|
||||||
// root: node.root,
|
|
||||||
// });
|
|
||||||
|
|
||||||
child = new RProxyNode(
|
child = new RProxyNode(
|
||||||
() => {
|
() => {
|
||||||
const rootRNode = getRootRNode(node);
|
node.get();
|
||||||
// 依赖根
|
|
||||||
rootRNode.get();
|
|
||||||
|
|
||||||
return getRNodeVal(node)[key];
|
return getRNodeVal(node)[key];
|
||||||
},
|
},
|
||||||
|
@ -78,7 +70,5 @@ export function getOrCreateChildRNode(node: RProxyNode<any>, key: string | symbo
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
child.track();
|
|
||||||
|
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { createProxy } from './proxy/RProxyHandler';
|
||||||
import { getRNodeVal, getRootRNode, setRNodeVal } from './RNodeAccessor';
|
import { getRNodeVal, getRootRNode, setRNodeVal } from './RNodeAccessor';
|
||||||
import { preciseCompare } from './comparison/InDepthComparison';
|
import { preciseCompare } from './comparison/InDepthComparison';
|
||||||
import { isObject } from './Utils';
|
import { isObject } from './Utils';
|
||||||
import {RNode, Root, runEffects} from "./RNode";
|
import { RNode, Root, runEffects } from './RNode';
|
||||||
|
|
||||||
export interface RNodeOptions {
|
export interface RNodeOptions {
|
||||||
root?: Root<any> | null;
|
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) {
|
setByArrayModified(value: T) {
|
||||||
const prevValue = this.getValue();
|
const prevValue = this.getValue();
|
||||||
|
|
||||||
|
@ -104,7 +97,7 @@ export class RProxyNode<T = any> extends RNode<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getValue() {
|
getValue() {
|
||||||
return getRNodeVal(this);
|
return this._value;
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue(value: T) {
|
setValue(value: T) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -63,6 +63,35 @@ describe('test reactive', () => {
|
||||||
expect(yCalledTimes).toBe(2);
|
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', () => {
|
it('reactive is a obj', () => {
|
||||||
const rObj = reactive({ count: 1 });
|
const rObj = reactive({ count: 1 });
|
||||||
|
|
||||||
|
@ -110,7 +139,6 @@ describe('test reactive', () => {
|
||||||
|
|
||||||
a.set(2);
|
a.set(2);
|
||||||
expect(c.a.read()).toBe(2);
|
expect(c.a.read()).toBe(2);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reactive is a array, watch', () => {
|
it('reactive is a array, watch', () => {
|
||||||
|
|
Loading…
Reference in New Issue