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

Add local-component example #239

Merged
merged 1 commit into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions local-component/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.db
*.py[cod]
.web
__pycache__/
assets/external
7 changes: 7 additions & 0 deletions local-component/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Local Component

This example shows how to create a React component in a JS file along with a
Reflex wrapper in python that allows its use in a Reflex app.

The custom component is `local_component/hello.js` and is a standard React
component that displays a greeting with some additional functionality.
Binary file added local-component/assets/favicon.ico
Binary file not shown.
Empty file.
34 changes: 34 additions & 0 deletions local-component/local_component/hello.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* This is an example inline React component that demonstrates how to pass a subset of props
* to the top-level `div` element.
*
* This component also demonstrates how to use `useMemo` to memoize the message
* string, and an event handler that updates a state variable when the user
* right-clicks on the component.
*/
import React, { useMemo, useState } from 'react';

// React.forwardRef is used to allow the parent component to pass a ref to this component.
export const Hello = React.forwardRef((props, ref) => {
// Extract the `name` prop that we care about, and pass any other props directly to the top `div`.
const {name, ...divProps} = props;

// This state variable controls whether the message will be all caps.
const [cap, setCap] = useState(false);

// Memoize the message to avoid re-calculating it on every render. Only re-calculate
// when `cap` or `name` changes.
const message = useMemo(() => {
const message = `Hello${name ? ` ${name}` : ""}!`;
return cap ? message.toUpperCase() : message;
}, [cap, name])

return (
// Pass ref and remaining props to the top-level `div`.
<div ref={ref} {...divProps}>
<h1 onContextMenu={(e) => {e.preventDefault(); setCap((cap) => !cap)}}>
{message}
</h1>
</div>
)
})
23 changes: 23 additions & 0 deletions local-component/local_component/hello.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""This component wraps the local `hello.js` which is supplied as an external asset."""
import reflex as rx

# Because the top-level element in Hello is a `<div>` and all other props are passed
# directly through, we can subclass `Div` to get the base behavior automatically.
from reflex.components.el.elements.typography import Div

# Defining the asset with a module-relative path will result in copying the
# file into a subdir of the .web/public directory
component_asset = rx._x.asset("hello.js")


class Hello(Div):
# To access the JS as an import, use an absolute path (rooted in `.web`)
# through `/public` where the assets exist in the frontend build.
library = f"/public/{component_asset}"

tag = "Hello"

name: rx.Var[str]


hello = Hello.create
76 changes: 76 additions & 0 deletions local-component/local_component/local_component.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import reflex as rx

from .hello import hello


class State(rx.State):
open: bool = False
who: str = "world"
saved_value: str = ""

def handle_open_change(self, open):
if open:
self.saved_value = self.who
else:
self.who = self.saved_value
self.open = open

def handle_submit(self, form_data):
who = form_data.get("who")
self.saved_value = who or "world"
self.handle_open_change(open=False)


def popover_editor(trigger):
return rx.popover.root(
rx.popover.trigger(rx.box(trigger)),
rx.popover.content(
rx.form.root(
rx.input(
placeholder="Who are you?",
name="who",
on_change=State.set_who,
),
rx.popover.close(
rx.button(display="none"),
),
on_submit=State.handle_submit,
),
),
open=State.open,
on_open_change=State.handle_open_change,
)


def index() -> rx.Component:
return rx.container(
rx.color_mode.button(position="top-right"),
rx.vstack(
rx.heading("Local Component Example", size="9"),
popover_editor(
hello(
name=State.who,
id="greeting", # React.forwardRef lets us handle refs
title="Click to change name. Right-click to toggle caps.",
on_context_menu=rx.console_log("Yes we pass events through").prevent_default,
background_color=rx.color_mode_cond(
light="papayawhip",
dark="rebeccapurple",
),
),
),
spacing="5",
justify="center",
min_height="85vh",
),
rx.logo(),
rx.button(
"Scroll to Greeting",
on_click=rx.scroll_to("greeting"),
margin_top="150vh",
),
)


app = rx.App()
app.add_page(index)
1 change: 1 addition & 0 deletions local-component/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
reflex>=0.5.4
5 changes: 5 additions & 0 deletions local-component/rxconfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import reflex as rx

config = rx.Config(
app_name="local_component",
)
Loading