diff --git a/packages/inula-reactive/src/RNode.ts b/packages/inula-reactive/src/RNode.ts index a6ffe307..fe80e811 100644 --- a/packages/inula-reactive/src/RNode.ts +++ b/packages/inula-reactive/src/RNode.ts @@ -56,7 +56,7 @@ export class RNode implements Signal { private observers: RNode[] | null = null; // 被谁用 private sources: RNode[] | null = null; // 使用谁 - private state: State; + protected state: State; isEffect = false; cleanups: ((oldValue: T) => void)[] = []; @@ -121,12 +121,6 @@ export class RNode implements Signal { } set(fnOrValue: T | ((prev: T) => T)): void { - if (this.fn) { - this.removeParentObservers(0); - this.sources = null; - this.fn = undefined; - } - const prevValue = this.getValue(); const value = isFunction(fnOrValue) ? fnOrValue(prevValue) : fnOrValue; diff --git a/packages/inula-reactive/src/RNodeCreator.ts b/packages/inula-reactive/src/RNodeCreator.ts index cfefecb5..9dd75cb9 100644 --- a/packages/inula-reactive/src/RNodeCreator.ts +++ b/packages/inula-reactive/src/RNodeCreator.ts @@ -16,15 +16,15 @@ import { isPrimitive } from './Utils'; import { RNode } from './RNode'; import { Fn, NonFunctionType, Signal } from './Types'; -import { Proxy, RProxyNode } from './RProxyNode'; +import { DeepReactive, RProxyNode } from './RProxyNode'; import { getRNodeVal } from './RNodeAccessor'; export function createReactive(raw?: T): Signal; export function createReactive(raw?: T): Signal; export function createReactive(raw?: T): Signal; export function createReactive(raw?: T): Signal; -export function createReactive | Array | symbol>(raw?: T): Proxy; -export function createReactive(raw?: T): Proxy | Signal { +export function createReactive | Array | symbol>(raw?: T): DeepReactive; +export function createReactive(raw?: T): DeepReactive | Signal { if (isPrimitive(raw) || raw === null || raw === undefined) { return new RNode(raw, { isSignal: true }); } else { @@ -55,6 +55,12 @@ export function getOrCreateChildProxy(value: unknown, parent: RProxyNode, k } export function getOrCreateChildRNode(node: RProxyNode, key: string | symbol): RProxyNode { + if (node.isComputed && !node.parent) { + const root = node.read(); + node.root = { + $: root + }; + } let child = node.children?.get(key); if (!child) { diff --git a/packages/inula-reactive/src/RProxyNode.ts b/packages/inula-reactive/src/RProxyNode.ts index 2157b6ab..213db8e4 100644 --- a/packages/inula-reactive/src/RProxyNode.ts +++ b/packages/inula-reactive/src/RProxyNode.ts @@ -14,13 +14,13 @@ */ import { createProxy } from './proxy/RProxyHandler'; -import { getRNodeVal, getRootRNode, setRNodeVal } from './RNodeAccessor'; +import { setRNodeVal } from './RNodeAccessor'; import { preciseCompare } from './comparison/InDepthComparison'; import { isObject } from './Utils'; -import { RNode, Root, runEffects } from './RNode'; -import { Computation, ProxyRNode, Signal } from './Types'; +import { Dirty, RNode, Root, runEffects } from './RNode'; +import { Computation, Signal } from './Types'; -export type Proxy = T extends Record +export type DeepReactive = T extends Record ? SignalProxy : T extends () => infer Return ? ComputationProxy @@ -56,7 +56,7 @@ export class RProxyNode extends RNode { key: KEY | null; children: Map | null = null; - proxy: Proxy = null; + proxy: DeepReactive = null; extend: any; // 用于扩展,放一些自定义属性 @@ -64,11 +64,9 @@ export class RProxyNode extends RNode { constructor(fnOrValue: (() => T) | T, options?: RNodeOptions) { super(fnOrValue, options); - this.isComputed = options?.isComputed || false; - // Proxy type should be optimized - this.proxy = createProxy as unknown as Proxy; + this.proxy = createProxy(this) as unknown as DeepReactive; this.parent = options?.parent || null; this.key = options?.key as KEY; this.root = options?.root || {}; @@ -119,6 +117,9 @@ export class RProxyNode extends RNode { } setValue(value: T) { + if (this.parent) { + this.state = Dirty; + } setRNodeVal(this, value); } } diff --git a/packages/inula-reactive/src/proxy/RProxyHandler.ts b/packages/inula-reactive/src/proxy/RProxyHandler.ts index 48a20bcd..1d2f6cf7 100644 --- a/packages/inula-reactive/src/proxy/RProxyHandler.ts +++ b/packages/inula-reactive/src/proxy/RProxyHandler.ts @@ -17,7 +17,7 @@ import { getOrCreateChildProxy } from '../RNodeCreator'; import { getRNodeVal } from '../RNodeAccessor'; import { isArray } from '../Utils'; import { RNode } from '../RNode'; -import {Proxy, RProxyNode} from '../RProxyNode'; +import { DeepReactive, RProxyNode } from '../RProxyNode'; const GET = 'get'; const SET = 'set'; @@ -44,7 +44,7 @@ const MODIFY_ARR_FNS = new Set([ // 数组的遍历方法 const LOOP_ARR_FNS = new Set(['forEach', 'map', 'every', 'some', 'filter', 'join']); -export function createProxy(proxyNode: T): Proxy { +export function createProxy(proxyNode: T): DeepReactive { return new Proxy(proxyNode, { get, set, diff --git a/packages/inula-reactive/src/utils/printNode.ts b/packages/inula-reactive/src/utils/printNode.ts index 4bd1896b..7eff51f3 100644 --- a/packages/inula-reactive/src/utils/printNode.ts +++ b/packages/inula-reactive/src/utils/printNode.ts @@ -21,7 +21,7 @@ export function printNode(signal: RNode) { if (signal.isEffect) { name = `Effect${signal.fn.name ?? ''}`; } else { - name = `Computation(${(signal as unknown as any).key.toString()})`; + name = `Computation(${(signal as unknown as any).key?.toString() ?? ''})`; } } else { name = 'Signal'; diff --git a/packages/inula-reactive/tests/reactive.test.ts b/packages/inula-reactive/tests/reactive.test.ts index 2ef0a4fb..f3f119ad 100644 --- a/packages/inula-reactive/tests/reactive.test.ts +++ b/packages/inula-reactive/tests/reactive.test.ts @@ -90,6 +90,32 @@ describe('test reactive', () => { expect(yWatch).toBeCalledTimes(3); }); + it('overwrite array reactive should keep reactive', () => { + const pos = reactive([0, 0]); + + const xWatch = jest.fn(); + watch(() => { + xWatch(pos[0].get()); + }); + const yWatch = jest.fn(); + watch(() => { + yWatch(pos[1].get()); + }); + expect(xWatch).toBeCalledTimes(1); + expect(yWatch).toBeCalledTimes(1); + + pos.set([1, 1]); + expect(xWatch).toBeCalledTimes(2); + expect(yWatch).toBeCalledTimes(2); + + pos.set([2, 1]); + expect(xWatch).toBeCalledTimes(3); + expect(yWatch).toBeCalledTimes(2); + + pos.set([2, 2]); + expect(xWatch).toBeCalledTimes(3); + expect(yWatch).toBeCalledTimes(3); + }); it('reactive is a obj', () => {