Skip to content

New API for reactpy.html #1279

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

Closed
Archmonger opened this issue Feb 14, 2025 · 1 comment
Closed

New API for reactpy.html #1279

Archmonger opened this issue Feb 14, 2025 · 1 comment
Labels
priority-2-moderate Should be resolved on a reasonable timeline. release-major Warrents a major release

Comments

@Archmonger
Copy link
Contributor

Archmonger commented Feb 14, 2025

Current Situation

Currently, the reactpy.html API feels a bit too wordy/verbose. When code formatters hit ReactPy code, it results in a lot of lines wasted to nothing but brackets or parenthesis.

To demonstrate, just look at how many lines of code the following example is:

html.div(
    {
        "className": "modal fade",
        "id": "exampleModal",
        "tabIndex": "-1",
        "aria-labeledby": "exampleModalLabel",
        "aria-hidden": "true",
    },
    html.div(
        {"className": "modal-dialog"},
        html.div(
            {"className": "modal-content"},
            modal_header(title="Modal Title"),
            modal_body(),
            html.div(
                {"className": "modal-footer"},
                html.hr(),
                [
                    html.button(
                        {
                            "type": "button",
                            "className": "btn btn-secondary",
                            "data-bs-dismiss": "modal",
                            "aria-label": "Close",
                        },
                        "Close",
                    ),
                    html.button(
                        {
                            "type": "button",
                            "className": "btn btn-primary",
                            "data-bs-dismiss": "modal",
                        },
                        "Save Changes",
                    ),
                ],
            ),
        ),
    ),
)

Proposed Actions

Here are some interfaces that have potential to be more compact.

Note that with both of the interfaces below, aince underscores are an invalid symbol in ReactJS properties, they would be automatically translated into hyphens. Single trailing underscores (ex _my_prop) would be automatically stripped to allow for for reserved keywords like for and while. Perhaps double underscore could work as an escape hatch denotation if underscores are truly wanted for that key value?

Context Manager API

with html.div(
    className="modal fade",
    _id="exampleModal",
    tabIndex="-1",
    aria_labeledby="exampleModalLabel",
    aria_hidden="true",
):
    with html.div(className="modal-dialog"):
        with html.div(className="modal-content"):
            modal_header(title="Modal Title")
            modal_body()
            with html.div(className="modal-footer"):
                html.hr()
                html.button(
                    "Close",
                    _type="button",
                    className="btn btn-secondary",
                    data_bs_dismiss="modal",
                    aria_label="Close",
                )
                html.button(
                    "Save Changes",
                    _type="button",
                    className="btn btn-primary",
                    data_bs_dismiss="modal",
                )

This has the added benefit of removing a lot of parenthesis, but comes at the cost of potentially feeling unnatural to users converting from ReactJS to ReactPy. As a note, what would traditionally be a list of elements with the old API would be done via for loops with this context manager API.

This interface design could be inspired by NiceGUI or Textual.

Props within kwargs and children within call args.

html.div(
    className="modal fade",
    _id="exampleModal",
    tabIndex="-1",
    aria_labeledby="exampleModalLabel",
    aria_hidden="true",
)(
    html.div(className="modal-dialog")(
        html.div(className="modal-content")(
            modal_header(title="Modal Title"),
            modal_body(),
            html.div(className="modal-footer")(
                html.hr(),
                [
                    html.button(
                        _type="button",
                        className="btn btn-secondary",
                        data_bs_dismiss="modal",
                        aria_label="Close",
                    )("Close"),
                    html.button(
                        _type="button",
                        className="btn btn-primary",
                        data_bs_dismiss="modal",
                    )("Save Changes"),
                ],
            ),
        )
    )
)

The biggest change is that ReactJS props would now be kwargs, and children would exist within a separate set of parenthesis call.


Previous discussions:

@Archmonger Archmonger added flag-triage Not prioritized. priority-2-moderate Should be resolved on a reasonable timeline. release-major Warrents a major release and removed flag-triage Not prioritized. labels Feb 14, 2025
@Archmonger
Copy link
Contributor Author

Archmonger commented Feb 17, 2025

After discussing and prototyping several different interface designs, the limitations surrounding other interfaces end up being too cumbersome to maintain.

  1. Any interface that users **kwargs instead of a dict for props can get frustrating to use due to Python reserved keywords.
  2. Context managers would add significant memory/performance overhead.
    • Note to future onlookers, if implementing this feature as an external package, you'll likely need to create a new component decorator (ex. @context_component). This decorator will initialize a ContextVar within each component that stores the component tree.
    • Additionally, this feature would require creating a subclass of reactpy.html that supports adding client elements to the ContextVar within the __enter__ method (for with html(...) support, and the __next__ method (for yield html(...) support.
  3. From a technical perspective, having children exist in square brackets (reactpy.html(my_attribute="value")["my_child"]) is the best of the bunch since it prevents users from using the unpacking operator (**), which is dangerous to use within ReactPy. But ultimately this interface adds too much syntax noise.

@Archmonger Archmonger closed this as not planned Won't fix, can't repro, duplicate, stale Feb 17, 2025
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
Projects
None yet
Development

No branches or pull requests

1 participant