Skip to content

[Draft] A Better Custom JS Interface #1001

Open
@rmorshea

Description

@rmorshea

Current Situation

There are a couple problems with the current JS interface:

  • Confusing to implement
  • Not versioned
  • Does not allow for components imported by other sources to be rendered as children

The last piece is a fairly significant blocker for allowing ReactPy to work seamlessly with JS since users may want to combine components from several different libraries together.

Proposed Actions

The new interface should allow custom JS modules to be implemented in the following way:

// inside my-component-lib.js
import React from "react";
import { ImportedElement } from "@reactpy/client";
// need to access all exports in this module (i think you can do self imports?)
import components import "./my-component-lib.js";

export bind(node) {
  root = React.createRoot(node);
  return {
    version: 1,
    renderVdom(model, context) =>
      root.render(
        <ImportedElement
          model={model}
          client={context.client}
          components={components}
        />),
    unmount: root.unmount,
  }
}

export function MyComponent(props) {
  ...
}

In typescript the module would conform to the following spec:

type ImportSource { bind: ( node: HTMLElement ) => ImportSourceBinding };

type ImportSourceBinding {
  version: number;
  renderVdom: (model: ReactPyVdom, context: { client: ReactPyClient  }) => void;
  unmount: () => void;
}

This seems simpler to understand than the current bind() function that returns an object with the methods:

  • create(type, props, children)
  • render(component)
  • unmount().

Where components are constructed with create and then passed to render.

With that said, the simpler interface comes at the cost of requiring more effort to implement. This comes down to the fact that, ReactPy wouldn't do any work to help render the model. Presently, ReactPy will render props passed to the create function which has the benefit that, model.eventHandlers will have already been turned into usable callbacks.

The new interface would basically make infeasible to write simple examples like the "super simple chart" seen in the docs today without importing some utilities from @reactpy/client. On way to remedy that could be to all ImportSourceBinding to have a renderSimple function that is mutually exclusive with renderVdom. The renderSimple function could look something like this:

type ImportSourceBinding {
  ...
  renderSimple?: (node: HTMLElement, type: string, props: { [key: string]: any }) => void;
}

Where, as is the case currently, prop would contain realized callbacks that were rendered from model.eventHandlers. Import sources that have a renderSimple function instead of a renderVdom function would not support component children.

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority-2-moderateShould be resolved on a reasonable timeline.release-majorWarrents a major releasetype-javascriptRelated to client-side code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions