Merge pull request !119 from Hoikan/reactive
This commit is contained in:
openInula-robot 2024-01-24 08:29:10 +00:00 committed by Gitee
commit f2bae1dd1f
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
6 changed files with 69 additions and 34 deletions

View File

@ -31,4 +31,4 @@ export {
computed,
isReactiveObj,
untrack
}
};

View File

@ -26,7 +26,6 @@ export type State = typeof Fresh | typeof Check | typeof Dirty;
type NonClean = typeof Check | typeof Dirty;
export interface RNodeOptions {
root?: Root<any> | null;
isSignal?: boolean;
isEffect?: boolean;
isComputed?: boolean;

View File

@ -17,7 +17,7 @@ import { isPrimitive } from './Utils';
import { RNode } from './RNode';
import { ProxyRNode } from './Types';
import { RProxyNode } from './RProxyNode';
import {getRNodeVal, getRootRNode} from "./RNodeAccessor";
import { getRNodeVal, getRootRNode } from './RNodeAccessor';
export type Reactive<T = any> = RNode<T> | Atom<T>;
@ -61,18 +61,21 @@ export function getOrCreateChildRNode(node: RProxyNode<any>, key: string | symbo
// root: node.root,
// });
child = new RProxyNode(() => {
const rootRNode = getRootRNode(node);
// 依赖根
rootRNode.get();
child = new RProxyNode(
() => {
const rootRNode = getRootRNode(node);
// 依赖根
rootRNode.get();
return getRNodeVal(node)[key];
}, {
isComputed: true,
parent: node,
key: key,
root: node.root,
});
return getRNodeVal(node)[key];
},
{
isComputed: true,
parent: node,
key: key,
root: node.root,
}
);
}
child.track();

View File

@ -25,7 +25,7 @@ export interface RNodeOptions {
isEffect?: boolean;
isComputed?: boolean;
isProxy?: boolean;
parent?: RNode<any> | null;
parent?: RProxyNode<any> | null;
key?: KEY | null;
equals?: (a: any, b: any) => boolean;
}
@ -55,14 +55,12 @@ export class RProxyNode<T = any> extends RNode<T> {
this.key = options?.key as KEY;
this.root = options?.root || {};
if (this.parent && !this.parent.children) {
this.parent.children = new Map();
if (this.parent) {
if (!this.parent.children) {
this.parent.children = new Map();
}
this.parent.children.set(this.key, this);
}
if (this.isComputed) {
this.update();
}
}
compare(prevValue: any, value: any) {
@ -109,7 +107,7 @@ export class RProxyNode<T = any> extends RNode<T> {
return getRNodeVal(this);
}
setValue(value: any) {
setValue(value: T) {
setRNodeVal(this, value);
}
}

View File

@ -30,7 +30,7 @@ export function isPrimitive(obj: unknown): boolean {
return obj != null && type !== 'object' && type !== 'function';
}
export function isFunction<T extends (...prev: any) => any>(obj: unknown): obj is T {
export function isFunction<T extends (...prev: any) => any>(obj: unknown): obj is Function {
return typeof obj === 'function';
}

View File

@ -1,31 +1,66 @@
import { reactive, computed, watch } from '../index';
describe('test reactive', () => {
it('two signals, one computed', () => {
it('computation should work with two reactive', () => {
const a = reactive(7);
const b = reactive(1);
let callCount = 0;
const c = computed(() => {
const product = computed(() => {
callCount++;
return { a: a.get() * b.get() };
return { value: a.get() * b.get() };
});
watch(() => {
console.log(a.get());
});
// computed should be lazy
expect(callCount).toBe(0);
expect(a.read()).toBe(7);
a.set(2);
expect(c.a.read()).toBe(2);
expect(product.value.read()).toBe(2);
b.set(3);
expect(c.a.get()).toBe(6);
expect(product.value.get()).toBe(6);
expect(callCount).toBe(2);
expect(callCount).toBe(3);
c.read();
expect(callCount).toBe(3);
product.read();
// computed function should not be invoked
expect(callCount).toBe(2);
});
it('computations should be triggered when source is same reactive', () => {
const pos = reactive({ x: 0, y: 0 });
let xCalledTimes = 0;
let yCalledTimes = 0;
const x = computed(() => {
xCalledTimes++;
return pos.x.get();
});
const y = computed(() => {
yCalledTimes++;
return pos.y.get();
});
expect(x.read()).toBe(0);
expect(y.read()).toBe(0);
expect(xCalledTimes).toBe(1);
expect(yCalledTimes).toBe(1);
// when pos.x changed, x should be triggered and y should not
pos.x.set(1);
expect(x.read()).toBe(1);
expect(y.read()).toBe(0);
expect(xCalledTimes).toBe(2);
expect(yCalledTimes).toBe(1);
// when pos.y changed, y should be triggered and x should not
pos.y.set(1);
expect(x.read()).toBe(1);
expect(y.read()).toBe(1);
expect(xCalledTimes).toBe(2);
expect(yCalledTimes).toBe(2);
});
it('reactive is a obj', () => {