[Proof Of Concept] JSX/TSX Support
See original GitHub issueI went to write something like this library just a few weeks ago, love that I found it instead!
I’d really like to see TSX support to make the library a little bit more intuitive for people use to the react way of doing things.
It would be cool to see a new submodule, something like “ivi-jsx” that handles the transition but makes it easy to exclude if people would prefer not to use it.
I threw together this quick proof of concept file:
import * as ivi from "ivi";
import * as html from "ivi-html";
export interface ElementProps {
className?: string;
style?: ivi.HTMLStyleElementAttrs;
children: ivi.OpNode<ivi.ElementData<any>>[] | ivi.OpNode<ivi.ElementData<any>>
}
export function createElement<Props extends ElementProps>(name:string|((props: Props) => ivi.OpNode<any>), props?: Props, ...children: ivi.OpNode<ivi.ElementData<Props>>[]): ivi.OpNode<ivi.ElementData<Props>> {
if (typeof name === "string") {
if (!html[name]) {
throw new Error(`HTML element "${name}" not found!`);
}
const thisProps = props || {} as Props;
const thisClass = thisProps["className"];
delete thisProps.className;
return html[name](thisClass || ivi._, thisProps, children || ivi._);
} else {
props.children = children;
return name(props);
}
}
export function createComponent<Props, State>(render: (state: ivi.OpState<ivi.ComponentHooks<State>>) => (props: Props) => ivi.OpNode<ivi.ElementData<Props>> | ivi.OpNode<ivi.ElementData<Props>>[]) {
return ivi.component(render);
}
export function TrackByKey<T>(props: {items: T[], render: (item: T) => [any, ivi.OpNode|ivi.OpNode[]]}) {
return ivi.TrackByKey(props.items.map(p => {
const [key, element] = props.render(p);
return ivi.key(key, element);
}));
}
In addition to the file above, you’ll need to drop this into the tsconfig:
"jsxFactory": "createElement",
"jsx": "react",
Now the blog example:
import { createComponent, createElement, TrackByKey } from "ivi-jsx";
import * as ivi from "ivi";
const Blog = createComponent((c) => {
return (props: {posts: any[]}) => {
return [
<TrackByKey items={props.posts} render={(post) => {
return [post.id, <h2>{post.title}</h2>]
}} />,
<hr />,
<TrackByKey items={props.posts} render={(post) => {
return [post.id, [<h3>{post.title}</h3>, <p>{post.content}</p>]]
}} />
];
}
});
const posts = [
{ id: 1, title: "Hello World", content: "Welcome to learning ivi!" },
{ id: 2, title: "Installation", content: "You can install ivi from npm." }
];
ivi.render(<Blog posts={posts} />, document.getElementById("app")!);
So far the only weird part I can see is the components must have their children passed down inside the props. So you’d have to either adjust the library internals or do components like this:
const Container = createComponent((c) => {
return (props) => {
return <div className="test">{props.children}</div>
}
});
ivi.render(<Container>Hello<Container/>, document.getElementById("app")!);
// kicks out: <div class="test">Hello</div>
I haven’t dug super deep into the implications of doing things like this, the prototype I hacked together “seems” to work but I’d really like to get your input on things.
I’d be more than happy to put together a more thought out PR if this looks like a good direction.
Issue Analytics
- State:
- Created 4 years ago
- Comments:6 (3 by maintainers)
Top GitHub Comments
Even if we can fix html/svg issue with some special prefix like
<svg.a>
, there are also custom elements and element prototypeselementProto()
, so it won’t be able to interop with ivi libraries that weren’t written with JSX in mind.Custom Elements:
Element Prototypes:
But I think that this issues are possible to solve with custom JSX/TSX transformer and there are many other libraries that using custom JSX/TSX transformers, so I don’t think that this issues are relevant for JSX, it just requires a different JSX transformer.
What I think will be hard to do is creating interop workarounds for components that doesn’t use
{}
as props, for example:Such components will be impossible to consume from JSX without creating wrappers that will convert
{ text, href }
into[text, href]
.I understand that, and I completely agree. If my primary goal with this library were to gain attention from the community, I could just create an efficient library with React API.
But from my point of view, JSX just adds too much unnecessary constraints and limits composition model. Also, because many component APIs doesn’t look ergonomic with JSX, we can often see some crazy stuff like:
Thanks Boris, appreciate the clarifications. Seems like JSX support is definitely outside the scope of what you want the library to be doing.