Skip to content

Commit 6b8e7c7

Browse files
committed
Release 0.2.0
1 parent f4cf75e commit 6b8e7c7

6 files changed

Lines changed: 65 additions & 80 deletions

File tree

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
An **En**gine for **D**OM **R**ecombobulation.
33

44
```jsx
5-
import { createRoot, useState } from 'endr';
5+
import { createRender, useState } from 'endr';
66

77
const Root = () => {
88
const [count, setCount] = useState(0);
@@ -15,7 +15,7 @@ const Root = () => {
1515
);
1616
};
1717

18-
createRoot(document.body).render(<Root />);
18+
createRender(document.body)(<Root />);
1919
```
2020

2121
# Why?
@@ -88,3 +88,12 @@ being found.
8888
`<AllMyChildren />` or any descendents throws an exception.
8989
- React's `Suspense` can be recreated with `Try` by awaiting all thrown
9090
promises, if desired.
91+
- `createRender` replaces `createRoot`. `createRender` returns a render function
92+
that can be called as often as desired to re-render at the root. To unmount
93+
all rendered components, simply call render with no children.
94+
```js
95+
import { createRender } from 'endr';
96+
const render = createRender(document.body);
97+
render(<MyDisappearingApp />);
98+
setTimeout(() => render(), 5000);
99+
```

docs/dist.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/index.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
Portal,
55
Try,
66
createContext,
7-
createRoot,
7+
createRender,
88
memo,
99
useCallback,
1010
useContext,
@@ -246,7 +246,16 @@ const Root = () => {
246246
</div>
247247
</div>
248248
<button
249-
onclick={root.unmount}
249+
onclick={() =>
250+
render(
251+
<div style={{ padding: '1rem', textAlign: 'center' }}>
252+
<div style={{ color: 'white', marginBottom: '1rem' }}>
253+
Unmounted!
254+
</div>
255+
<button onclick={() => render(<Root />)}>Remount</button>
256+
</div>
257+
)
258+
}
250259
style={{ position: 'fixed', bottom: '1rem', right: '1rem' }}
251260
>
252261
Unmount
@@ -255,9 +264,9 @@ const Root = () => {
255264
);
256265
};
257266

258-
const root = createRoot(
267+
const render = createRender(
259268
/** @type {Element} */ (document.getElementById('root')).attachShadow({
260269
mode: 'open'
261270
})
262271
);
263-
root.render(<Root />);
272+
render(<Root />);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "endr",
33
"type": "module",
4-
"version": "0.1.28",
4+
"version": "0.2.0",
55
"author": "Casey Foster <c@sey.me>",
66
"license": "MIT",
77
"repository": {

src/index.js

Lines changed: 38 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,6 @@
310310
* }} Effect
311311
*/
312312

313-
/** @typedef {{ render: (children: Children) => void; unmount: () => void }} Root */
314-
315313
/**
316314
* @typedef {{
317315
* afterEffects: Vnode[];
@@ -412,12 +410,6 @@ const isEmpty = value => value == null || value === false || value === '';
412410
*/
413411
const isFunction = value => typeof value === 'function';
414412

415-
/**
416-
* @param {unknown} value
417-
* @returns {value is (unknown)[]}
418-
*/
419-
const isArray = value => Array.isArray(value);
420-
421413
/**
422414
* @param {unknown} value
423415
* @returns {value is {[key: string]: unknown}}
@@ -622,7 +614,7 @@ export const useState = initial => {
622614
return value;
623615
}
624616
]);
625-
return /** @type {State<T>} */ ([...state]);
617+
return state;
626618
};
627619

628620
/**
@@ -647,7 +639,9 @@ const depsChanged = (before, after) => {
647639
export const useCallback = fn => {
648640
const ref = useRef(() => fn);
649641
ref.current = fn;
650-
return useMemo(() => /** @type {T} */ ((...args) => ref.current(...args)));
642+
return useMemo(
643+
() => /** @type {T} */ ((...args) => ref.current.apply(null, args))
644+
);
651645
};
652646

653647
/**
@@ -727,10 +721,7 @@ const queueUpdate = vnode => {
727721
queueMicrotask(() => {
728722
const batch = updates.sort(batchComparator);
729723
queues.updates = [];
730-
for (let i = 0; i < batch.length; ++i) {
731-
const vnode = batch[i];
732-
if (vnode.state === 1) update(vnode);
733-
}
724+
for (const vnode of batch) if (vnode.state === 1) update(vnode);
734725
flush(queues);
735726
});
736727
}
@@ -755,7 +746,11 @@ const getDefs = vnode => {
755746
[currentVnode, effectIndex, refIndex] = prev;
756747
}
757748
}
758-
return isEmpty(children) ? [] : isArray(children) ? children : [children];
749+
return isEmpty(children)
750+
? []
751+
: Array.isArray(children)
752+
? children
753+
: [children];
759754
};
760755

761756
/** @param {unknown} def */
@@ -793,7 +788,7 @@ const getNodes = ({ child, node }) => {
793788

794789
/** @type {(Element | Text)[]} */
795790
const nodes = [];
796-
for (; child; child = child.sibling) nodes.push(...getNodes(child));
791+
for (; child; child = child.sibling) nodes.push.apply(nodes, getNodes(child));
797792
return nodes;
798793
};
799794

@@ -815,8 +810,7 @@ const update = vnode => {
815810

816811
if (vnode.effects) {
817812
try {
818-
for (let i = 0; i < vnode.effects.length; ++i) {
819-
const effect = vnode.effects[i];
813+
for (const effect of vnode.effects) {
820814
if (effect.after && effect.before) {
821815
effect.before();
822816
effect.before = undefined;
@@ -900,9 +894,7 @@ const update = vnode => {
900894
}
901895
vnode.lastNode = vnode.node ?? prevNode ?? null;
902896

903-
if (prevChildren) {
904-
for (const child of prevChildren.values()) remove(child);
905-
}
897+
if (prevChildren) for (const child of prevChildren.values()) remove(child);
906898

907899
if (vnode.effects) afterEffects.push(vnode);
908900
};
@@ -918,8 +910,7 @@ const remove = (vnode, removeNode = true) => {
918910

919911
if (effects) {
920912
try {
921-
for (let i = 0; i < effects.length; ++i) {
922-
const effect = effects[i];
913+
for (const effect of effects) {
923914
if (effect.before) {
924915
effect.before();
925916
effect.before = undefined;
@@ -938,29 +929,24 @@ const remove = (vnode, removeNode = true) => {
938929

939930
if (node) {
940931
if (props.ref) props.ref.current = null;
941-
if (removeNode) vnode.queues.removes.unshift(node);
932+
if (removeNode) vnode.queues.removes.push(node);
942933
}
943934
};
944935

945936
/** @param {Queues} queues */
946937
const flush = queues => {
947-
const { afterEffects, inserts, nodeUpdates, removes } = queues;
948-
949-
for (let i = removes.length - 1; i >= 0; --i) removes[i].remove();
938+
for (const node of queues.removes) node.remove();
950939

951940
queues.removes = [];
952941

953-
for (let i = 0; i < inserts.length; ++i) {
954-
const vnode = inserts[i];
942+
for (const vnode of queues.inserts) {
955943
const { node, parentNode, prevNode, props } = vnode;
956944
const before = prevNode ? prevNode.nextSibling : parentNode.firstChild;
957945
if (node && !node.parentNode) {
958946
updateNode(node, emptyProps, props);
959947
parentNode.insertBefore(node, before);
960948
} else {
961-
const nodes = getNodes(vnode);
962-
for (let i = 0; i < nodes.length; ++i) {
963-
const node = nodes[i];
949+
for (const node of getNodes(vnode)) {
964950
if (node === before || node.nextSibling === before) break;
965951

966952
// @ts-expect-error moveBefore is available on modern browsers
@@ -971,18 +957,15 @@ const flush = queues => {
971957

972958
queues.inserts = [];
973959

974-
for (let i = 0; i < nodeUpdates.length; ++i) updateNode(...nodeUpdates[i]);
960+
for (const args of queues.nodeUpdates) updateNode.apply(null, args);
975961

976962
queues.nodeUpdates = [];
977963

978-
for (let i = 0; i < afterEffects.length; ++i) {
979-
const vnode = afterEffects[i];
980-
const effects = /** @type {NonNullable<Vnode['effects']>} */ (
981-
vnode.effects
982-
);
964+
for (const vnode of queues.afterEffects) {
983965
try {
984-
for (let j = 0; j < effects.length; ++j) {
985-
const effect = effects[j];
966+
for (const effect of /** @type {NonNullable<Vnode['effects']>} */ (
967+
vnode.effects
968+
)) {
986969
if (effect.after) {
987970
effect.before = effect.after();
988971
effect.after = undefined;
@@ -997,16 +980,7 @@ const flush = queues => {
997980
};
998981

999982
/** @param {ParentNode} parentNode */
1000-
export const createRoot = parentNode => {
1001-
/** @type {Queues} */
1002-
const queues = {
1003-
afterEffects: [],
1004-
inserts: [],
1005-
nodeUpdates: [],
1006-
removes: [],
1007-
updates: []
1008-
};
1009-
983+
export const createRender = parentNode => {
1010984
/** @type {Vnode} */
1011985
const vnode = {
1012986
catch: defaultCatch,
@@ -1021,27 +995,24 @@ export const createRoot = parentNode => {
1021995
parent: null,
1022996
parentNode,
1023997
prevNode: /** @type {Element} */ (parentNode.lastChild),
1024-
props: {},
1025-
queues,
998+
props: { children: undefined },
999+
queues: {
1000+
afterEffects: [],
1001+
inserts: [],
1002+
nodeUpdates: [],
1003+
removes: [],
1004+
updates: []
1005+
},
10261006
refs: null,
10271007
sibling: null,
10281008
state: 1,
10291009
type: Fragment
10301010
};
10311011

1032-
update(vnode);
1033-
flush(queues);
1034-
1035-
return /** @type {Root} */ ({
1036-
render: children => {
1037-
vnode.props = { children };
1038-
update(vnode);
1039-
flush(queues);
1040-
},
1041-
1042-
unmount: () => {
1043-
remove(vnode);
1044-
flush(queues);
1045-
}
1046-
});
1012+
/** @param {Children} [children] */
1013+
return children => {
1014+
vnode.props.children = children;
1015+
update(vnode);
1016+
flush(vnode.queues);
1017+
};
10471018
};

types/index.d.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export function useState<T>(initial: T | (() => T)): State<T>;
3737
export function useCallback<T extends AnyFunction>(fn: T): T;
3838
export function memo<T extends Component>(Component: T, memo?: typeof defaultMemo): T;
3939
export function useContext<T extends Context<any>>(Context: T): T["value"];
40-
export function createRoot(parentNode: ParentNode): Root;
40+
export function createRender(parentNode: ParentNode): (children?: Children) => void;
4141
export type Recursive<T> = T | RecursiveArray<T>;
4242
export type RecursiveArray<T> = Recursive<T>[];
4343
export type AnyFunction = (...args: any[]) => unknown;
@@ -272,10 +272,6 @@ export type Effect = {
272272
after: AfterEffect | undefined;
273273
deps: unknown[] | undefined;
274274
};
275-
export type Root = {
276-
render: (children: Children) => void;
277-
unmount: () => void;
278-
};
279275
export type Queues = {
280276
afterEffects: Vnode[];
281277
inserts: Vnode[];

0 commit comments

Comments
 (0)