Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Draft] A Better Custom JS Interface #1001

Open
rmorshea opened this issue May 27, 2023 · 8 comments
Open

[Draft] A Better Custom JS Interface #1001

rmorshea opened this issue May 27, 2023 · 8 comments
Labels
priority-2-moderate Should be resolved on a reasonable timeline. release-major Warrents a major release type-javascript Related to client-side code

Comments

@rmorshea
Copy link
Collaborator

rmorshea commented May 27, 2023

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.

@rmorshea rmorshea added the flag-triage Not prioritized. label May 27, 2023
@rmorshea rmorshea changed the title A Better Custom JS Interface [Draft] A Better Custom JS Interface May 27, 2023
@Archmonger
Copy link
Contributor

Let's schedule a meeting to talk through this interface and run some simulated development scenarios.

@rmorshea
Copy link
Collaborator Author

rmorshea commented May 27, 2023

I think I've been going about this from the wrong angle. If implementing the custom JS component interface is going to require that you import some utilities from @reactpy/client, we can imagine utilities that would make doing so significantly easier:

import { createBinding } from "@reactpy/client";

const binding = createBinding();

binding.add( function SomeComponent(props) { ... } )
binding.add( function AnotherComponent(props) { ... } )

export binding;

@Archmonger
Copy link
Contributor

We forgot to talk about how this bind API will play into the build system

@rmorshea rmorshea added priority-2-moderate Should be resolved on a reasonable timeline. type-javascript Related to client-side code release-major Warrents a major release and removed flag-triage Not prioritized. labels Jun 7, 2023
@nsosupport
Copy link

We forgot to talk about how this bind API will play into the build system

Good

@rmorshea
Copy link
Collaborator Author

This library seems perfect for what we're trying to do: https://github.com/samwillis/nodejs-pypi

@Archmonger
Copy link
Contributor

Not only that, but the nodsjs-bin PyPi package seems perfect for us to use for ReactPy Build.

@himself65
Copy link

Is there a good way that js side could call the python component? For example

export const App = () => {
  return (
    <main>
      <PythonComponent id='Counter'/>
    </main>
  )
}
@component
def Counter():
    number, set_number = use_state(0)

    def handle_click(event):
        set_number(number + 1)
        set_number(number + 1)
        set_number(number + 1)

    return html.div(
        html.h1(number),
        html.button({"on_click": handle_click}, "Increment"),
    )

will be into

<main>
  <h1>0</h1>
  <button>increment</button>
</main>

@Archmonger
Copy link
Contributor

Archmonger commented Feb 26, 2024

@himself65 That feature currently doesn't exist. If we developed some sort of compiling step, is it technically possible that feature could exist in the future. Especially since we've already proven ReactPy can run client-side via this prototype implementation.

If you wish to discuss this further, please create a post in GitHub discussions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority-2-moderate Should be resolved on a reasonable timeline. release-major Warrents a major release type-javascript Related to client-side code
Projects
None yet
Development

No branches or pull requests

4 participants