Releases: localvoid/ivi
v0.27.0
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
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
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
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()
andCONTENT()
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
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 undocumentedtrue
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 functioncontainsDOMElement()
. - Added
hasDOMElementChild()
function. - Removed autofix for Mouse Event bubbling in iOS
- Added
VisitNodesDirective
to get a better control overvisitNodes()
algorithm. - Added
onTransitionRun()
andonTransitionStart()
events.
v0.22.0
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
- 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
0.15.0
Bug Fixes
- vdom: add missing exports (4ab29a9)
Features
- scheduler: add optional argument
flags
torender()
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
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()
topropsChanged()
(6434f5a)
Features
- core: add underscore
_
as an alias toundefined
(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()
ands()
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()
andgetComponentInstanceFromVNode()
were renamed togetDOMNode()
and
getComponent()
- vdom:
VNode
methodsvalue()
andunsafeHTML()
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()
andnextFrameAfter()
functions were removed. - scheduler: DOM reader and animation tasks were replaced with
beforeUpdate()
andafterUpdate()
task lists. - vdom: Component lifecycle method
newPropsReceived()
renamed topropsChanged()
. - vdom: VNode methods
a()
ands()
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.