Skip to content

Dars Framework v1.7.7

Choose a tag to compare

@ZtaMDev ZtaMDev released this 07 Dec 22:59
· 62 commits to CrystalMain since this release

Release Notes v1.7.7

Component Lifecycle Hooks, Dynamic VRefs & Safer Runtime Updates

Installation

pip install --upgrade dars-framework

What's New

Component Lifecycle Hooks (onMount, onUpdate, onUnmount)

This release introduces component-level lifecycle hooks, available both for regular components and @FunctionComponent:

  • New props on components (and FunctionComponents):
    • onMount
    • onUpdate
    • onUnmount
  • Hooks accept dScript & functions or inline JS strings.
  • The VDOM now includes a lifecycle block per node with:
    • Hook type (inline/dscript).
    • JS code ready to be executed in the runtime.

Dynamic createComp/deleteComp with Lifecycle and Events

The dynamic backend helpers are now fully compatible with lifecycle, events and VRefs:

  • createComp(target, root, position="append"):

    • Uses VDomBuilder to serialize the full component into VDOM.
    • Injects the _events map produced by the exporter for that subtree.
    • In the runtime, runtime.createComponent(rootId, vdom, pos):
      • Builds actual DOM from the VDOM.
      • Rehydrates events for the whole subtree (_attachEventsForVNode).
      • Registers lifecycle in __lifecycle and fires onMount for all nodes with hooks.
      • Calls DarsHydrate(el) so the rest of bindings (V(), VRefs, useDynamic, etc.) attach to the dynamic subtree as well.
  • deleteComp(id):

    • Calls runtime.deleteComponent(id).
    • The runtime runs onUnmount for that id before removing the node and its VDOM entry.

Result: components created and destroyed with createComp/deleteComp have the same lifecycle behavior and event rehydration as components rendered in the initial VDOM.

onUpdate wired into dynamic changes and VRefs

The onUpdate hook now fires in two key scenarios:

  1. Dynamic state changes (runtime change()):

    • When Dars.change({ id, dynamic: true, ... }) is called (e.g. via updateComp()), the runtime:
      • Applies changes to text, html, style, attrs, classes, etc.
      • Notifies watchers (watch() / internals).
      • Calls _runLifecycle(id, 'onUpdate') whether the element exists or the change is purely logical.
  2. VRef changes via updateVRef():

    • New runtime helper: Dars.updateVRef(selector).

    • The JS generated by updateVRef(...) now, after updating the DOM and window.__DARS_VREF_VALUES__, does:

      if (window.Dars && window.Dars.updateVRef) {
          window.Dars.updateVRef(selector);
      }
    • Dars.updateVRef(selector):

      • Finds all elements matching the selector.
      • For each, walks up the DOM tree until it finds the first ancestor with lifecycle registered.
      • Runs onUpdate for that id (once per id), allowing parent components to react to shared VRef changes.

Improved dynamic VRefs (setVRef + updateVRef)

Several adjustments ensure setVRef() works equally well in the initial render and in components created dynamically via createComp:

  • setVRef(value, selector) now creates a VRefValue whose metadata is preserved in the VDOM:
    • Text(text=setVRef(0, ".dyn_count")) serializes as:
      • text: "0" (initial value).
      • class: "dyn_count" auto-added when the selector is of type .class.
    • The runtime registers window.__DARS_VREF_VALUES__['.dyn_count'] = 0 during hydration.
  • This works the same when that Text is part of a component created with createComp: the VDOM includes both the initial value and the class, and updateVRef(".dyn_count", ...) can find and update all instances correctly.

New equal() helper for expressions with V()

To make it easier to compose numeric and literal expressions with V(), this release adds the equal() helper in dars.hooks.value_helpers:

from dars.hooks.value_helpers import V, equal

# Sumar 1 a un VRef
Button(
    "Increment",
    on_click=updateVRef(".dyn_count", V(".dyn_count").int() + 1),
)

# Resetear a 0 usando equal()
Button(
    "Reset",
    on_click=updateVRef(".dyn_count", equal(0)),
)

# Combinar con otra expresión basada en V()
expr = V(".a").int() + equal(V(".b").int())
updateVRef(".result", expr)
  • equal(value) normalizes any value (literal, V(), MathExpression, etc.) into a MathExpression, so it integrates into the operation tree with the same async semantics and NaN validation as the rest of the expression system.

Note: to reset a VRef it's better to use equal(0) directly as the target value instead of adding it to the current value.

Fixes in updateComp() and JS generation

JS generation for updateComp() (dynamic backend) has been hardened so it works correctly with complex expressions containing // comments and async IIFEs such as those generated by updateVRef(...):

  • The internal code in updateComp() now:
    • No longer collapses whitespace/newlines in the JS payload, avoiding cases where // comments consume the rest of the line.
    • Safely strips the trailing ; from dScript.code before inlining it into the change({ ... }) payload.
  • This allows updateComp() to be combined with complex scripts (including those generated by V() / updateVRef) without causing SyntaxError in app.js.

Documentation updates

The hooks and components documentation has been updated to reflect these changes:

  • LandingPage/documentation/markdown/hooks.md:
    • Extended Pythonic Value Helpers section with the new equal() helper and examples together with V() and updateVRef.
  • LandingPage/documentation/markdown/components.md:
    • The Runtime Component Manipulation (createComp / deleteComp) and new Component Lifecycle Hooks sections explain that dynamically created subtrees:
      • Rehydrate events.
      • Register and execute lifecycle hooks (onMount/onUpdate/onUnmount).
      • Are compatible with VRefs and the current VDOM system.