Skip to content

Releases: localvoid/ivi

v0.27.0

01 May 05:22
Compare
Choose a tag to compare

Optimizations

Reduced memory consumption by operation nodes. All properties are now inlined into operation nodes and there are three different operation node shapes. Almost all callsites should stay in monomorphic state, except for the one that accessing operation type in the mount and update functions, it will be in polymorphic state and it is ok as long as it doesn't transition into megamorphic state.

Bug Fixes

  • Fixed nextNode assignment in a dirty checking algorithm when it is going through DOM elements.

v0.26.0

29 Apr 10:47
Compare
Choose a tag to compare

Optimizations

Reduced memory consumption in component hooks, refs and portals.

API that is used for this optimizations is now public. This API is using several objects: TASK_TOKEN, SELECT_TOKEN and UNMOUNT_TOKEN to identify who is invoking a callback function. Callbacks registered with scheduleMicrotask() will receive TASK_TOKEN as a first argument, hooks added with useUnmount() will receive UNMOUNT_TOKEN. SELECT_TOKEN is used internally to optimize useSelect().

Immediately executed useEffect()

useEffect() hook is now immediately executed when it is invoked in the internal component "update" function. It makes it fully deterministic to prevent unexpected bugs when useEffect() is used to listen some observable value that can generate an event before microtask with an effect is executed. When it is immediately executed, it will guarantee that we don't miss any events generated by an observable value.

v0.25.0

25 Apr 10:43
Compare
Choose a tag to compare

Context

New context implementation provides a slightly different API that makes it possible to remove dirty context checking during updates and dirty checking, doesn't require to provide value type when retrieving context values, and doesn't use global namespace for context keys.

BEFORE:

const C = component((c) => {
  const getValue = useSelect((c) => context<{ value: number }>().value);
  return () => getValue();
});

render(
  Context({ value: 123 },
    C(),
  ),
  container,
);

AFTER:

const Value = contextValue<number>();
const C = component((c) => {
  const getValue = useSelect((c) => Value.get());
  return () => getValue();
});

render(
  Value.set(123,
    C(),
  ),
  container,
);

Reconciler

Children reconciliation algorithm for fragments were changed to dynamically add/remove at the end of the fragment instead of reinstantiating entire fragment when fragment length is changed.

v0.24.0

22 Apr 07:40
Compare
Choose a tag to compare

shouldUpdate

All shouldUpdate functions are replaced with their inverse function areEqual.

APIs affected by this change:

  • component(_, shouldUpdate)
  • statelessComponent(_, shouldUpdate)
  • useSelect(_, shouldUpdate)
  • useEffect(_, shouldUpdate)
  • useLayoutEffect(_, shouldUpdate)
  • useMutationEffect(_, shouldUpdate)
  • memo(_, shouldUpdate)
  • selector(_, shouldUpdate)

Tests

New package ivi-jest adds a collection of useful tools for testing with jest library. All ivi tests were completely
rewritten using a new ivi-jest library.

Scheduler

ivi-scheduler and ivi-test-scheduler packages were removed. Old unit tests were using package aliasing to mock scheduler behavior and now it is not needed anymore.

Events

Removed optional bubbling, all events are now always bubble.

Bug Fixes

  • Fast path for TrackByKey hasn't been executed correctly when nodes doesn't change their positions. It is hard to test because it is still works correctly even when this fast path doesn't execute.
  • Prevent VALUE() and CONTENT() attribute directives from accepting numbers, they should work only with string values.
  • shouldUpdate functions hasn't been executed correctly in component hooks and caused more updates than it was necessary.

v0.23.0

12 Apr 15:49
Compare
Choose a tag to compare

Synthetic Events

Synthetic event internals were heavily redesigned to reduce overall complexity and improve API flexibility for customsynthetic events.

Custom synthetic events can now inject their own behavior into event flow of native events. It should significantly improve performance when there are many custom synthetic events as it won't be necessary to traverse virtual dom tocollect dispatch targets for each custom synthetic event.

API for creating custom synthetic events is still an unstable API and it is most likely that there will be changes in the future, but it is an extremely useful API that solves alot of problems with UI applications.

Native events are no longer wrapped in a SyntheticNativeEvent object

BEFORE

onClick((ev) => {
  console.log(ev.native.target); // target
});

AFTER:

onClick((ev) => {
  console.log(ev.target);
});

EventFlags is removed

To stop event propagation event handler should return true value.

BEFORE:

onClick((ev) => {
  return EventFlags.PreventDefault | EventFlags.StopPropagation;
});

AFTER:

onClick((ev) => {
  ev.preventDefault();
  return true;
});

currentTarget is now accessible as a second argument

BEFORE

onClick((ev) => {
  console.log(ev.node); // currentTarget
});

AFTER:

onClick((ev, currentTarget) => {
  console.log(currentTarget);
});

SyntheticEvent interface is removed

SyntheticEvent interface had two properties: node and timestamp. node were used to assign current target, it is replaced with an additional argument in all event handler functions. timestamp is a leftover from an old synthetic events implementation that tried to fix cross-browser quirks. For many custom synthetic events this property doesn't have any value and it custom event implementor should decide when timestamp value is necessary.

beforeNativeEvent() and afterNativeEvent() are removed

It is replaced with an addNativeEventMiddleware().

addNativeEventMiddleware(MOUSE_DOWN, (event, next) => {
  // beforeNativeEvent...
  next(event);
  // afterNativeEvent...
});

Portals

Portals were completely redesigned and moved to ivi-portal package. Portals now correctly propagate context through portal entries.

import { _, render, component, invalidate, Events, onClick, } from "ivi";
import { div, button } from "ivi-html";
import { portal } from "ivi-portal";

const MODAL = portal();

const App = component((c) => {
  let showModal = false;
  const showEvent = onClick(() => { showModal = true; invalidate(c); });

  return () => (
    [
      showModal ? MODAL.entry(div("modal", _, "This is being rendered inside the #modal-root div.")) : null,
      Events(showEvent,
        button(_, _, "Show modal"),
      ),
    ]
  );
});

render(App(), document.getElementById("app"));
render(MODAL.root, document.getElementById("modal-root"));

Error Handling

Unhandled exceptions raised inside of a catchError() block are now considered as userspace bugs and will change application state to "error". When application state is "error", all entry points wrapped in catchError() will be blocked to prevent potential security issues because it is impossible to infer which part of an application state caused a bug.

All ivi entry points like render(), synthetic event handlers, etc are wrapped with a catchError() block.

Creating custom functions wrapped with a catchError()

const entryFn = catchError((arg1, arg2) => {
  // ...
});

entryFn(arg1, arg2);

Reconciler

  • Fixed bug when direct child node of a Context() node triggers replace operation.
  • Fixed bug when strictly equal direct child node of an HTML/SVG element doesn't trigger deep dirty checking.
  • Fixed bug when useUnmount() hook hasn't been receiving an undocumented true value as a first argument. It is an unstable feature that can be used for micro optimizations in custom hooks.
  • Added shortcuts for DOM property accesors that should reduce megamorphic call-sites.

Misc

  • Replaced containsRelatedTarget() with a generic function containsDOMElement().
  • Added hasDOMElementChild() function.
  • Removed autofix for Mouse Event bubbling in iOS
  • Added VisitNodesDirective to get a better control over visitNodes() algorithm.
  • Added onTransitionRun() and onTransitionStart() events.

v0.22.0

15 Mar 10:41
Compare
Choose a tag to compare

Added support for events:

  • onBeforeInput()
  • onTransitionCancel()
  • onTransitionEnd()

Breaking Changes

Global Variables replaced with Environment Variables

__IVI_DEBUG__ and __IVI_TARGET__ were replaced with process.env.NODE_ENV !== "production" and process.env.IVI_TARGET to support parcel bundler Issue #10.

v0.21.0

11 Feb 09:07
Compare
Choose a tag to compare
  • Full support for server-side rendering renderToString()
  • Reduced code size

Breaking Changes

Global Variables

DEBUG and TARGET were renamed to __IVI_DEBUG__ and __IVI_TARGET__ to prevent name conflicts with variables that can be used in different packages.

useSelect()

Context argument is removed from selectors, context() function should be used to access current context.

function useSelect<T>(
  c: StateNode,
  selector: (props?: undefined, prev?: T | undefined) => T,
): () => T;
function useSelect<T, P>(
  c: StateNode,
  selector: (props: P, prev?: T | undefined) => T,
  shouldUpdate?: undefined extends P ? undefined : (prev: P, next: P) => boolean,
): undefined extends P ? () => T : (props: P) => T;

Attribute Directives

Attribute directives were changed to support server-side rendering:

interface AttributeDirective<P> {
  v: P;
  u?: (element: Element, key: string, prev: P | undefined, next: P | undefined) => void;
  s?: (key: string, next: P) => void;
}

s() method can be used to alter renderToString() behavior.

VALUE() directive

VALUE() directive now works only with HTMLInputElement elements. New CONTENT() directive should be used to assign value for HTMLTextArea elements.

VALUE() directive emits value="${v}" attribute when rendered to string.

CONTENT() directive emits children string <textarea>${v}</textarea> when rendered to string.

v0.16.0

23 Oct 07:29
Compare
Choose a tag to compare

Bug Fixes

  • events: ignore non-vdom nodes when dispatching events (5904715)

Features

  • package: deprecate unpkg modules (27e8379)
  • test: add support for text content (d1b02ca)
  • vdom: bring back optimization for singular text nodes (b39555d)
  • vdom: clone subtree for prerendered elements (2f9bfb3)

0.15.0

21 Jun 13:32
Compare
Choose a tag to compare

Bug Fixes

  • vdom: add missing exports (4ab29a9)

Features

  • scheduler: add optional argument flags to render() function (6be1314)
  • scheduler: fail early when scheduler hasn't been configured (1eeb3b2)
  • scheduler: improve invalidate functions (8f5a328)
  • scheduler: make default invalidate handler NOOP (ec6a479)
  • scheduler: remove obsolete function isHidden() (23ab024)
  • state: simplify store implementation (79c31a2)
  • vdom: add support for removing events in EVENT() (437e4ce)
  • vdom: add syncable value AUTOFOCUS() (18460e7)
  • vdom: add syncable value EVENT() (2546f8e)
  • vdom: move text node factory t() from html package to ivi (d53d8e9)
  • vdom: remove direct dependency with a complex scheduler (34807fd)

0.14.0

16 Jun 06:18
Compare
Choose a tag to compare

Bug Fixes

  • events: fix incorrect imports ivi-events => events (368c387)
  • gestures: fixes browser quirk with TouchMove events (9fedf9e)
  • test: fix rendering attrs to snapshot (syncable values) (9f1de27)
  • types: support number types for css property "bottom" (4ff9486)
  • vdom: remove obsoleted checks in DEBUG mode (4274d57)

Code Refactoring

  • vdom: rename newPropsReceived() to propsChanged() (6434f5a)

Features

  • core: add underscore _ as an alias to undefined (35834f4)
  • debug: add DEBUG pubsub to expose internal state (466aba2)
  • gestures: add multitouch transform gesture recognizer (28991fe)
  • gestures: change gesture recognizers lifecycle (af2b86a)
  • gestures: fix bugs, add more gesture recognizers (a63b27f)
  • gestures: fully working gesture events prototype (b310dcf)
  • scheduler: add beforeUpdate / afterUpdate repeatable tasks (f599405)
  • scheduler: remove frame task queue after (d3c4f72)
  • scheduler: remove visibility observers (d816fda)
  • types: add types for specialized properties in attribute lists (69cc9a2)
  • vdom: add mapIterable() to support iterable objects (c09c5cb)
  • improve dev mode checks (4e7db28)
  • vdom: add universal syncable value PROPERTY() (111c309)
  • vdom: don't trigger updated() for all parents (5c75401)
  • vdom: new attribute syncing algorithm (564957d)
  • vdom: remove support for null nodes returned from mapIterable() (e3c88a5)
  • vdom: rename instance getters (bbcf255)
  • vdom: replace VNode methods a() and s() with factory args (4f00a52)
  • remove factories for obsolete elements, improve types for attrs (c2b9173)
  • rename INPUT_VALUE() and INPUT_CHECKED() to VALUE() and CHECKED() (943a414)

Performance Improvements

  • events: improve event dispatcher algorithm (352287a)

BREAKING CHANGES

  • vdom: getDOMInstanceFromVNode() and getComponentInstanceFromVNode() were renamed to getDOMNode() and
    getComponent()
  • vdom: VNode methods value() and unsafeHTML() were removed.

Before:

input("", { type: "checked" }).value(true);
use("", { "xlink:href": "sprite.svg#a" });
div().unsafeHTML("abc");

After:

input("", { type: "checked", checked: CHECKED(true) });
use("", { "xlink:href": XLINK_ATTR("sprite.svg#a") });
div("", { unsafeHTML: UNSAFE_HTML("abc") });
  • scheduler: currentFrameAfter() and nextFrameAfter() functions were removed.
  • scheduler: DOM reader and animation tasks were replaced with beforeUpdate() and afterUpdate() task lists.
  • vdom: Component lifecycle method newPropsReceived() renamed to propsChanged().
  • vdom: VNode methods a() and s() were replaced with optional arguments for all element factory functions.

Before:

div("className").a({ id: "ID" }).s({ color: "red" });

After:

div("className", { id: "ID" }, { color: "red" });
  • vdom: updated() lifecycle is now triggered only for components that were updated. Parent components won't be
    receiving any information that their children were updated.

Initially it was implemented to solve use cases with jumping scroll
positions when children layout is modified. But there is a better way,
scheduler allows to register repeatable tasks that will be invoked
before any rendering to read from the DOM, and after all rendering is
done, this hooks solve this use case perfectly. And since I don't know
about any use cases that would require such behavior, it would be better
to reduce API surface. All major frameworks doesn't support such
behavior.