question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Proof Of Concept] JSX/TSX Support

See original GitHub issue

I 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:closed
  • Created 4 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
localvoidcommented, Mar 30, 2019

Even if we can fix html/svg issue with some special prefix like <svg.a>, there are also custom elements and element prototypes elementProto(), so it won’t be able to interop with ivi libraries that weren’t written with JSX in mind.

Custom Elements:

// ivi-google-map.js
// export const googleMap = htmlElementFactory("google-map");

import { googleMap } from "ivi-google-map";
render(googleMap(), container);

Element Prototypes:

// ivi-checkbox.js
// export const checkbox = elementProto(input(_, { type: "checkbox" }));

import { checkbox } from "ivi-checkbox"; 
render(checkbox("class-name"), container);

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:

const Link = component(
  () => ([text, href]) => a("link", { href }, text),
  shallowNotEqualArray, // shouldComponentUpdate
);

Such components will be impossible to consume from JSX without creating wrappers that will convert { text, href } into [text, href].

I would argue that many people (myself included) would trade a few features and efficiencies for being able to use JSX.

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:

<Table data={data}>
  <Table.Column id="name" />
  <Table.Column id="fname" />
</Table>
0reactions
only-clichescommented, Apr 5, 2019

Thanks Boris, appreciate the clarifications. Seems like JSX support is definitely outside the scope of what you want the library to be doing.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Introducing JSX - React
It is called JSX, and it is a syntax extension to JavaScript. We recommend using it with React to describe what the UI...
Read more >
Learning React proof of concept - javascript - Stack Overflow
My understanding is that a React app is drive by the state object? Elements of this state object can be passed to components...
Read more >
React JSX - W3Schools
JSX allows us to write HTML elements in JavaScript and place them in the DOM without any createElement() and/or appendChild() methods. JSX converts...
Read more >
beepsoft/grapesjs-react-component-example - GitHub
Proof of concept example of React component as a GrapesJS block and template. - GitHub - beepsoft/grapesjs-react-component-example: Proof of concept example ...
Read more >
Really, why React? - DEV Community ‍ ‍
Instead of HTML or template literals, React templates use, according to the docs, a "funny tag syntax [that] is neither a string nor...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found