1. Project Overview & Quickstart (aidenybai/react-scan)
File: docs/installation/cdn.md
CDN
You can choose one of the following URLs to initialize React Scan via CDN.
Usage
<script src="https://cdn.jsdelivr.net/npm/react-scan/dist/auto.global.js"></script>Available URLs
JSDelivr
https://cdn.jsdelivr.net/npm/react-scan/dist/auto.global.jsUNPKG
https://unpkg.com/react-scan/dist/auto.global.jsFile: docs/installation/next-js-page-router.md
NextJS Page Router Guide
As a script tag
Add the script tag to your pages/_document
Refer to the CDN Guide for the available URLs.
// pages/_document
import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html lang="en">
<Head>
<script src="https://unpkg.com/react-scan/dist/auto.global.js" />
{/* rest of your scripts go under */}
</Head>
<Main />
<NextScript />
</Html>
);
}As a module import
Add the following code to your App component in pages/_app:
// pages/_app
// react-scan must be the top-most import
import { scan } from "react-scan";
import { useEffect } from "react";
export default function App({ Component, pageProps }) {
useEffect(() => {
// Make sure to run React Scan after hydration
scan({
enabled: true,
});
}, []);
return <Component {...pageProps} />;
}If you want react-scan to also run in production, use the react-scan/all-environments import path
- import { scan } from "react-scan";
+ import { scan } from "react-scan/all-environments";2. Official Technical Reference & Guides (aidenybai/docs)
File: README.md
Moved to Million repo
Million Documentation
File: pages/docs/api/extra/html.mdx
title: HTML Helpers
HTML Helpers
`html` tagged templates
The html function creates a VNode from an HTML string, and makes it easier to write views without hyperscript or JSX. It accepts a tagged HTML string, meaning you can do interoplation like JSX with the ${expression} syntax.
import { render } from 'million';
import { html } from 'million/html';
render(document.body, html`Hello World`);Tag factories
You can import tag functions to write shorthands for elements. It is a great middle ground if you want that hyperscript style of development but also want to write less code.
import { render } from 'million';
import { div } from 'million/html';
render(document.body, div(['Hello World']));You can also construct your own reusable custom element factories if you want:
import { render } from 'million';
import { factory } from 'million/html';
const CUSTOM = factory('CUSTOM');
render(document.body, CUSTOM(['Hello World']));File: pages/docs/api/extra/morph.mdx
title: morph()
`morph()`
Syntax: morph(newDOMNode, prevDOMNode, hook = (el?, newVNode?, oldVNode?) => boolean)
Example: morph('Hello World', document.createElement('div'))
The morph function updates the DOM content by determing pinpoint changes through diffing a new DOM node with an old DOM node. It accepts two HTMLElement or Text. It also returns the resulting HTMLElement or Text.
import { m, patch, createElement } from 'million';
const node1 = document.createElement('div');
const node2 = document.createElement('div');
node2.textContent = 'Hello World';
morph(node2, node1); // -> Hello World
morph('Goodbye World', node2); // Hello World -> Goodbye WorldFile: pages/docs/api/extra/router.mdx
title: router()
`router()`
Syntax: router(rootSelector, routes)
Example: router()
There are two uses for the router() function:
1. Turning a Multi Page Application (MPA) into a Single Page Application (SPA)
The router() function initiates SPA like routing functionality for an MPA. This removes the necessity for page navigations and makes the application more responsive, while keeping multiple HTML files. Imagine Turbo Drive, but powered by Million, and significantly more simpler.
import { router } from 'million/router';
router();
// to only swap the body:
router('body');2. Creating client side routes for a SPA
router also accepts a routes parameter, which skips fetching if you have predefined virtual nodes. For example:
import { _, m } from 'million';
import { router, createRoute } from 'million/router';
router()
.setRoute(
'/',
createRoute(
m('div', _, ['You are at /', m('a', { href: '/foo' }, ['Go to /foo'])]),
),
)
.setRoute(
'/foo',
createRoute(
m('div', _, ['You are at /foo', m('a', { href: '/' }, ['Go to /'])]),
),
);`apply()` and `million:navigate` event
You can use a global event handler to listen on the million:navigate event.
window.addEventListener('million:navigate', (event) => {
console.log(`Navigating to ${event.detail.url}`);
});If you have client side scripts that run DOM modifications upon load, you can use the apply() function.
import { apply } from 'million/router';
apply((doc) => {
doc.title += ' - reloaded';
});File: pages/docs/api/extra/utils.mdx
title: Shared Utilities
Shared Utilities
`fromDomNodeToVNode()`
Syntax: fromDomNodeToVNode(node)
Example: fromDomNodeToVNode(document.createElement('div'))
The fromDomNodeToVNode function creates a VNode from DOM node. It accepts a DOM node.
import { render } from 'million';
import { fromDomNodeToVNode } from 'million/utils';
fromDomNodeToVNode(document.createElement('div'));{
tag: 'div';
}`fromStringToDomNode()`
Syntax: fromStringToDomNode(html)
Example: fromStringToDomNode('Hello World')
The fromStringToDomNode function creates an DOM node from an HTML string using DOMParser. It accepts a HTML string.
import { render } from 'million';
import { fromStringToDomNode } from 'million/utils';
fromStringToDomNode('Hello World').innerHTML; // -> Hello World`fromVNodeToString()`
Syntax: fromVNodeToString(vnode)
Example: fromVNodeToString({ tag: 'div' })
The fromVNodeToString function creates an HTML string from an VNode. It accepts a VNode.
import { m, _, render } from 'million';
import { fromVNodeToString } from 'million/utils';
fromStringToDomNode(m('div', _, ['Hello World'])) // -> Hello WorldFile: pages/docs/api/basics/m.mdx
title: m()
import VNodeViz from '../../../../components/examples/VNodeViz';
`m()`
Syntax: m(tag, props?, children?, flag?, delta?, hook?)
Example: m('div', { id: 'app' }, ['Hello World'])
It is recommended that you use m to create a VNode. It accepts a tag as a string, an optional props object, an optional array of children, and an optional flag.
import { m } from 'million';
const vnode = m('div', { id: 'app' }, ['Hello World']);{
tag: 'div',
props: {
id: 'app'
},
children: ['Hello World'],
}The tagName is stored under the tag, attributes and properties are stored under props, and the children are stored under children.
REPL
You can enter HTML and it will automatically convert to a VNode if you are confused on how the raw data representation looks.
Optimization via keys
Most of the time, the diffing and patching process is fast enough, but when dealing with a large amount of children, it is best to provide runtime hints through keys. You can attach a key under props. When patched, it will only diff props and children if the key is changed. For more advanced runtime diffing using keys, check out Flags.ELEMENT_KEYED_CHILDREN.
import { m } from 'million';
const vnode = m('div', { key: 'foo' }, ['Hello World']);{
tag: 'div',
props: {},
children: ['Hello World'],
key: 'foo',
}`className` and `style` props helpers
The className and style props need to be preprocessed using the className and style functions to convert objects to strings. The class object syntax allows for you to toggle classes based on a boolean value. The style object syntax allows you to set styles in a clean format.
import { m, className, style } from 'million';
const vnode = m(
'div',
{
className: className({ class1: true, class2: false, class3: 1 + 1 === 2 }),
style: style({ color: 'black', 'font-weight': 'bold' }),
},
['Hello World'],
);{
tag: 'div',
props: {
className: 'class1 class3',
style: 'color:black;font-weight:bold'
},
children: ['Hello World'],
}`kebab` props helper
Generally, the values of className and style props are objects in kebab-case. However, if you want to use camelCase for the keys of these props, you can use the kebab function to convert the keys from camelCase to kebab-case.
import { m, style, kebab } from 'million';
const vnode = style(kebab({ color: 'black', fontWeight: 'bold' }));{
tag: 'div',
props: {
style: 'color:black;font-weight:bold'
},
children: ['Hello World'],
}SVG support
SVGs are handled by default, but sometimes you need to attach SVG namespaces. SVGs are processed using the svg function to add ns props to the element and all of the children of that element.
import { m, svg } from 'million';
const vnode = svg(m('svg'));{
tag: 'svg',
props: {
ns: 'http://www.w3.org/2000/svg'
},
}Lifecycle `hook` properties
The lifecycle hook properties allows you to "hook in" to the diffing and patching process. There are four hooks: 'create', 'update', 'remove', and one 'diff' control flow hook. Every hook, if defined, must return a boolean, which will determine whether the code runs of not (true runs, false doesn't run).
import { _, m } from 'million';
const vnode = m(
'div',
_ /* props */,
_ /* children */,
_ /* flag */,
_ /* delta */,
{
create: (el, newVNode, oldVNode) => {
console.log('create was called!', el);
return true;
},
update: (el, newVNode, oldVNode) => {
console.log('update was called!', el);
return true;
},
remove: (el, newVNode, oldVNode) => {
console.log('remove was called!', el);
return true;
},
diff: (el, newVNode, oldVNode) => {
console.log('diff was called!', el);
return true;
},
},
);If you have multiple hooks, you can merge them easily with the mergeHooks function:
import { mergeHooks } from 'million';
const firstHook = {
create: () => {
console.log(1);
return true;
},
};
const secondHook = {
create: () => {
console.log(2);
return true;
},
update: () => {
console.log('Hello World!');
return true;
},
};
const finalBossHook = mergeHooks([firstHook, secondHook]);As a result, finalBossHook becomes:
const finalBossHook = {
create: () => {
console.log(1); // From firstHook
console.log(2); // From secondHook
return true;
},
update: () => {
console.log('Hello World!'); // from secondHook
return true;
},
};File: pages/docs/api/basics/render.mdx
title: render()
import Callout from 'nextra-theme-docs/callout';
import Counter from '../../../../components/examples/Counter';
`render()`
Syntax: render(parentElement, newVNode?, prevVNode?, hook?)
Example: render(document.body, m('div'))
The render function is a combonation of the patch() and createElement() functions. It takes a parent element and a vnode and returns a DOM node, and renders the vnode into the DOM.
Here's an example Counter app using render():
import { _, m, render } from 'million';
let seconds = 0;
setInterval(() => {
render(document.body, m('p', _, [`Time elapsed: ${seconds}`]));
seconds++;
}, 1000);render() function has a standard interface that is used in many Virtual DOM libraries. First argument is a DOM node that will be used as the parent DOM reference, and the second one is a Virtual DOM to render.
m() function will instantiate a "Virtual DOM" node for an element.
File: pages/docs/api/advanced/batch.mdx
title: batch()
`batch()`
Syntax: batch(timer)
Example: batch()
You can use batch to reschedule requestAnimationFrame and reduce unnecessary rerenders in short intervals. It accepts a timer as a number and returns a callback queuer function.
Note that
batchis should not be abused for every operation. Using it for every operation may delay the rendering of the page. Additionally, functionality may break if you specify a customoldVNodefor each operation.
batch works best when renders occur in rapid succession:
import { _, m, batch, render } from 'million';
const queueRender = batch();
queueRender(() => render(document.body, I'm not rendered));
queueRender(() => render(document.body, I'm not rendered));
queueRender(() => render(document.body, I'm rendered!));File: pages/docs/api/advanced/createElement.mdx
title: createElement()
`createElement()`
Syntax: createElement(vnode, attachField = true)
Example: createElement(m('div'))
The createElement function converts a VNode into a HTMLElement or Text. It accepts a VNode (VElement or string). This is generally used to initialize an root element to use as a reference during patching.
import { m, createElement, Flags } from 'million';
const vnode = m(
'div',
{ id: 'app' },
['Hello World'],
Flags.ELEMENT_TEXT_CHILDREN,
);
const el = createElement(vnode);
document.body.appendChild(el);Hello World`OLD_VNODE_FIELD` property
The OLD_VNODE_FIELD property is a global export on the Million namesplace, as is automatically attached to the new HTMLElement for reference during the patching process. You can disable this by setting the attachField parameter to false, if you are able to manage the old VNode state yourself.
File: pages/docs/api/advanced/delta.mdx
title: Deltas
`Deltas`
Syntax: Deltas.OPERATION(index)
Example: Deltas.CREATE(0)
Deltas are a way for the compile-time to optimize runtime operations by providing a set of predefined operations. This is useful for cases where you are performing consistent, predictable operations at a high interval, low payload situation.
There are three types of delta operations: Deltas.INSERT, Deltas.UPDATE, and Deltas.DELETE. You can provide a specified index to select the position of the VNode's children to be inserted, updated, or deleted.
Deltas.INSERTis used to add children at a selected index.
Syntax:Deltas.CREATE(index)
Example:Deltas.CREATE(0)Deltas.UPDATEis used to replace children at a selected index.
Syntax:Deltas.UPDATE(index)
Example:Deltas.UPDATE(0)Deltas.DELETEis used to remove children at a selected index.
Syntax:Deltas.REMOVE(index)
Example:Deltas.REMOVE(0)
You can load these operations into a delta, or an array. You can pass them inside the m function.
import { _, m, Deltas } from 'million';
const vnode = m('div', _, ['Hello World'], _, [
Deltas.CREATE(0),
Deltas.UPDATE(0),
]);{
tag: 'div',
children: ['Hello World'],
delta: [
[0 /* DeltaTypes.INSERT */, 0],
[1 /* DeltaTypes.UPDATE */, 0],
]
}