From 592d9a458ec0b570b9f63112c427153db8172c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Mon, 10 Jan 2022 13:49:12 +0100 Subject: [PATCH] [REF] move useState into component_node.ts --- src/component/component_node.ts | 37 +++++++++++++++++++++++++++++++-- src/index.ts | 4 ++-- src/reactivity.ts | 33 +++-------------------------- 3 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/component/component_node.ts b/src/component/component_node.ts index d1a188b2..78e155cb 100644 --- a/src/component/component_node.ts +++ b/src/component/component_node.ts @@ -1,6 +1,9 @@ import type { App, Env } from "../app/app"; import { BDom, VNode } from "../blockdom"; +import { clearReactivesForCallback, NonReactive, Reactive, reactive } from "../reactivity"; +import { batched, Callback } from "../utils"; import { Component, ComponentConstructor } from "./component"; +import { fibersInError, handleError } from "./error_handling"; import { Fiber, makeChildFiber, @@ -9,7 +12,6 @@ import { MountOptions, RootFiber, } from "./fibers"; -import { handleError, fibersInError } from "./error_handling"; import { applyDefaultProps } from "./props_validation"; import { STATUS } from "./status"; @@ -26,6 +28,37 @@ export function useComponent(): Component { return currentNode!.component; } +// ----------------------------------------------------------------------------- +// Integration with reactivity system (useState) +// ----------------------------------------------------------------------------- + +const batchedRenderFunctions = new WeakMap(); +/** + * Creates a reactive object that will be observed by the current component. + * Reading data from the returned object (eg during rendering) will cause the + * component to subscribe to that data and be rerendered when it changes. + * + * @param state the state to observe + * @returns a reactive object that will cause the component to re-render on + * relevant changes + * @see reactive + */ +export function useState(state: T): Reactive | NonReactive { + const node = getCurrent(); + let render = batchedRenderFunctions.get(node)!; + if (!render) { + render = batched(node.render.bind(node)); + batchedRenderFunctions.set(node, render); + // manual implementation of onWillDestroy to break cyclic dependency + node.willDestroy.push(clearReactivesForCallback.bind(null, render)); + } + return reactive(state, render); +} + +// ----------------------------------------------------------------------------- +// component function (used in compiled template code) +// ----------------------------------------------------------------------------- + export function component( name: string | typeof Component, props: any, @@ -72,7 +105,7 @@ export function component( } // ----------------------------------------------------------------------------- -// Component VNode +// Component VNode class // ----------------------------------------------------------------------------- type LifecycleHook = Function; diff --git a/src/index.ts b/src/index.ts index 11a3ed3f..cf045b8a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,11 +38,11 @@ export const blockDom = { export { App, mount } from "./app/app"; export { Component } from "./component/component"; -export { useComponent } from "./component/component_node"; +export { useComponent, useState } from "./component/component_node"; export { status } from "./component/status"; export { Memo } from "./memo"; export { xml } from "./app/template_set"; -export { useState, reactive, markRaw, toRaw } from "./reactivity"; +export { reactive, markRaw, toRaw } from "./reactivity"; export { useEffect, useEnv, useExternalListener, useRef, useChildSubEnv, useSubEnv } from "./hooks"; export { EventBus, whenReady, loadFile, markup } from "./utils"; export { diff --git a/src/reactivity.ts b/src/reactivity.ts index 2a49fadf..7fa124b9 100644 --- a/src/reactivity.ts +++ b/src/reactivity.ts @@ -1,6 +1,4 @@ -import { onWillDestroy } from "./component/lifecycle_hooks"; -import { ComponentNode, getCurrent } from "./component/component_node"; -import { batched, Callback } from "./utils"; +import { Callback } from "./utils"; // Allows to get the target of a Reactive (used for making a new Reactive from the underlying object) const TARGET = Symbol("Target"); @@ -17,7 +15,7 @@ export type Reactive = T & { [TARGET]: any; }; -type NonReactive = T & { +export type NonReactive = T & { [SKIP]: any; }; const objectToString = Object.prototype.toString; @@ -115,7 +113,7 @@ const callbacksToTargets = new WeakMap>(); * * @param callback the callback for which the reactives need to be cleared */ -function clearReactivesForCallback(callback: Callback): void { +export function clearReactivesForCallback(callback: Callback): void { const targetsToClear = callbacksToTargets.get(callback); if (!targetsToClear) { return; @@ -226,28 +224,3 @@ export function reactive( } return reactivesForTarget.get(callback) as Reactive; } - -const batchedRenderFunctions = new WeakMap(); -/** - * Creates a reactive object that will be observed by the current component. - * Reading data from the returned object (eg during rendering) will cause the - * component to subscribe to that data and be rerendered when it changes. - * - * @param state the state to observe - * @returns a reactive object that will cause the component to re-render on - * relevant changes - * @see reactive - */ -export function useState(state: T): Reactive | NonReactive { - const node = getCurrent(); - if (!batchedRenderFunctions.has(node)) { - batchedRenderFunctions.set( - node, - batched(() => node.render()) - ); - onWillDestroy(() => clearReactivesForCallback(render)); - } - const render = batchedRenderFunctions.get(node)!; - const reactiveState = reactive(state, render); - return reactiveState; -}