From 30d28670e87dd93eb37cb8186042095865928940 Mon Sep 17 00:00:00 2001 From: Mathieu Duckerts-Antoine Date: Mon, 15 Nov 2021 16:56:50 +0100 Subject: [PATCH] [REF] reactivity: new API --- src/reactivity.ts | 22 ++++++++++++-------- tests/reactivity.test.ts | 43 +++++++++++++++------------------------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/reactivity.ts b/src/reactivity.ts index 3f7f5662..938e3c60 100644 --- a/src/reactivity.ts +++ b/src/reactivity.ts @@ -14,11 +14,20 @@ const KEYS = Symbol("keys"); const ROOT = Symbol("root"); const SEED = Symbol("Seed"); -export function atom(source: any, observer: Observer) { +export function observe(source: any, observer: Observer): [Atom, Function] { + if (!isTrackable(source)) { + throw new Error("First argument is not trackable"); + } + const unregisterObserver = registerObserver(observer); + const newAtom = atom(source, observer); + return [newAtom, unregisterObserver]; +} + +function atom(source: any, observer: Observer) { return _atom(source, observer, true); } -export function _atom(source: any, observer: Observer, seed = false) { +function _atom(source: any, observer: Observer, seed = false) { if (isTrackable(source) && observerSourceAtom.has(observer)) { source = (source as any)[SOURCE] || source; const oldAtom = observerSourceAtom.get(observer)!.get(source); @@ -123,7 +132,7 @@ function isTrackable(value: any): boolean { ); } -export function registerObserver(observer: Observer) { +function registerObserver(observer: Observer) { if (!observerSourceAtom.get(observer)) { observerSourceAtom.set(observer, new Map()); } @@ -149,13 +158,10 @@ function unregisterObserverAtoms(observer: Observer, keepSeeds: boolean) { } export function useState(state: any): Atom { - if (!isTrackable(state)) { - throw new Error("Argument is not trackable"); - } const node = getCurrent()!; - const unregisterObserver = registerObserver(node); + const [newAtom, unregisterObserver] = observe(state, node); onWillUnmount(() => unregisterObserver()); - return atom(state, node); + return newAtom; } class ObserverSet { diff --git a/tests/reactivity.test.ts b/tests/reactivity.test.ts index 4d0ab3c7..b5752099 100644 --- a/tests/reactivity.test.ts +++ b/tests/reactivity.test.ts @@ -1,5 +1,5 @@ -import { Component, mount, onRender, onWillStart, onWillUpdateProps, useState } from "../src"; -import { atom, registerObserver } from "../src/reactivity"; +import { Component, mount, onRender, onWillStart, onWillUpdateProps } from "../src"; +import { observe, useState } from "../src/reactivity"; import { xml } from "../src/tags"; import { makeDeferred, @@ -9,10 +9,9 @@ import { snapshotEverything, } from "./helpers"; -function createAtom(value: any, observer?: any) { - observer = observer || (() => {}); - registerObserver(observer); - return atom(value, observer); +function createAtom(value: any, observer: any = () => {}) { + const [atom] = observe(value, observer); + return atom; } describe("Reactivity: atom", () => { @@ -54,15 +53,8 @@ describe("Reactivity: atom", () => { expect(Array.isArray(atom1)).toBe(true); }); - test("return value if not proxifiable", () => { - const atom1 = createAtom(1); - expect(atom1).toBe(1); - }); - - test("return value if observer is not registered", () => { - const obj = { a: 1 }; - const atom1 = atom(obj, () => {}); - expect(atom1).toBe(obj); + test("Throw error if value is not proxifiable", () => { + expect(() => createAtom(1)).toThrow("First argument is not trackable"); }); test("atom observer is called properly", async () => { @@ -270,16 +262,16 @@ describe("Reactivity: atom", () => { }); test("immediately returns primitive values", () => { - expect(createAtom(1)).toBe(1); - expect(createAtom("asf")).toBe("asf"); - expect(createAtom(true)).toBe(true); - expect(createAtom(null)).toBe(null); - expect(createAtom(undefined)).toBe(undefined); + expect(() => createAtom(1)).toThrowError(); + expect(() => createAtom("asf")).toThrowError(); + expect(() => createAtom(true)).toThrowError(); + expect(() => createAtom(null)).toThrowError(); + expect(() => createAtom(undefined)).toThrowError(); }); test("immediately returns dates", () => { const date = new Date(); - expect(createAtom(date)).toBe(date); + expect(() => createAtom(date)).toThrow("First argument is not trackable"); }); test("can observe object with some key set to null", async () => { @@ -294,8 +286,7 @@ describe("Reactivity: atom", () => { test("can reobserve object with some key set to null", async () => { let n = 0; const fn = () => n++; - const unregisterObserver = registerObserver(fn); - const atom1 = createAtom({ a: { b: null } } as any, fn); + const [atom1, unregisterObserver] = observe({ a: { b: null } } as any, fn); const atom2 = createAtom(atom1, fn); expect(atom2).toBe(atom1); expect(atom2).toEqual(atom1); @@ -846,9 +837,8 @@ describe("Reactivity: atom", () => { test("can unobserve a value", async () => { let n = 0; const cb = () => n++; - const unregisterObserver = registerObserver(cb); - const atom1 = createAtom({ a: 1 }, cb); + const [atom1, unregisterObserver] = observe({ a: 1 }, cb); atom1.a = atom1.a + 3; await nextMicroTick(); @@ -932,8 +922,7 @@ describe("Reactivity: atom", () => { test("notification is not done after unregistration", async () => { let n = 0; const observer = () => n++; - const unregisterObserver = registerObserver(observer); - const state = atom({ a: 1 } as any, observer); + const [state, unregisterObserver] = observe({ a: 1 } as any, observer); state.a = state.a; await nextMicroTick();