only-jsx
v0.1.3
Published
JSX runtime for Vanilla JS
Downloads
10
Readme
only-jsx
Another JSX runtime for Vanilla JS without react, vue, or something else
install and config
npm i only-jsx
and edit babel.config.js
or .babelrc
or other babel config:
...
plugins: [
...
[
'@babel/plugin-transform-react-jsx',
{
'runtime': 'automatic',
'importSource': 'only-jsx'
}
]
]
...
or tsconfig.json
...
"compilerOptions": {
...
"jsx": "react-jsx",
"jsxImportSource": "only-jsx",
}
...
then coding js or ts with jsx.
context
Some JSX elements require context from their parents to be rendered correctly. Examples are children of an SVG element that require a namespaceURI from the parent. The setContext method is designed to resolve this issue.
const SvgContext = (props: OptionsChildren, ctx: any) => (props.children as ContextFunc)(ctx);
export default (props: OptionsChildren) => {
setContext(SvgContext, {})
return <SvgContext><svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'>
<path d={props.children}></path>
</svg></SvgContext>;
}
or even simpler
export default props => {
setContext('svg', {})
return <svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'>
<path d={props.children}></path>
</svg>;
}
examples
The source codes are in the repository https://github.com/only-jsx/examples.
//index.jsx
import APP from './app';
function render() {
const appProps = {a:1, b:2};
function onClick() {
appProps.a++;
appProps.b++;
appProps.update();
}
const t = <>
Hello
<APP props={appProps}>In <span>APP</span></APP>
<button onclick={onClick}>Update APP</button>
</>;
window.onload = () => {
const e = document.getElementById('root');
e.replaceChildren(t);
}
}
render();
//app.jsx
export default ({props, children}) => {
const result = {};
const e = <div data-custom={props.b}>
<span>{props.a + props.b}</span>
<button onclick={onClick}>Button</button>
<div ref={result}></div>
<div id='counter' style='color: blue;'></div>
{children}
</div>;
let counter = 0;
function onClick() {
fetch('https://api.github.com/repos/only-jsx/jsx-runtime/contributors').then(data => {
data.json().then(contributors => {
counter++;
const c = contributors.map(user => <div>{user.login} {user.contributions}</div>)
result.current.replaceChildren(...c);
e.querySelector('#counter').replaceChildren(<span>{counter} requests</span>);
});
});
}
props.update = function () {
e.dataset.custom = this.b;
e.firstChild.innerText = this.a + this.b;
result.current.replaceChildren();
}
return e;
}
//index.tsx
import APP, {AppProps} from './app';
import {Fragment} from 'only-jsx/jsx-runtime';
function render() {
const root = document.getElementById('root');
function replace (e: HTMLElement ) {
root.replaceChildren(e);
}
const appProps: AppProps = {
a:1,
b:2,
replace,
};
function onClick() {
appProps.a++;
appProps.b++;
appProps.update?.();
}
const t = <Fragment>
Hello
<APP props={appProps}>In <span>APP</span></APP>
<button onclick={onClick}>Increase numbers</button>
</Fragment>;
window.onload = () => replace(t);
}
render();
//app.tsx
import styles from './app.module.css';
import {OptionsChildren} from 'only-jsx/jsx-runtime';
interface GhContrubutor {
login: string;
contributions: number;
}
export interface AppProps {
a: number;
b: number;
replace: (e: HTMLElement ) => void;
update?: () => void;
}
export default ({ props, children }: { props: AppProps, children?: OptionsChildren }) => {
const result: { current?: HTMLElement } = {};
const e = <div data-custom={props.b}>
<span>{props.a + props.b}</span>
<button onclick={onClickFetch}>Fetch</button>
<div id='counter' style='color: blue;'></div>
<div ref={result}></div>
{children}
<button onclick={onClickReplace}>Replace content</button>
</div>;
function onClickReplace() {
props.replace(<p>Content was completely replaced</p>);
}
let counter = 0;
function onClickFetch() {
fetch('https://api.github.com/repos/only-jsx/jsx-runtime/contributors').then(data => {
counter++;
data.json().then((contributors: GhContrubutor[]) => {
const c = contributors.map(user => <div>{user.login} {user.contributions}</div>)
result.current.replaceChildren(...c);
result.current.className = styles.result;
}).catch(err=>{
result.current.replaceChildren(err.message);
result.current.className = styles.error;
});
e.querySelector('#counter').replaceChildren(<span>Request #{counter}</span>);
});
}
props.update = function () {
e.dataset.custom = this.b;
e.firstChild.innerText = this.a + this.b;
}
return e;
}