Skip to content

Commit 79d2f78

Browse files
authored
fix wrapping docs again (#1380)
* add create method explanation * more fixes * add advanced section * fix
1 parent 5ed447d commit 79d2f78

File tree

2 files changed

+107
-5
lines changed

2 files changed

+107
-5
lines changed

docs/wrapping-react/library-and-tags.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ This is when we will define the most important attributes of the component:
2727
4. **lib_dependencies**: Any additional libraries needed to use the component.
2828
5. **is_default**: (Optional) If the component is a default export from the module, set this to `True`. Default is `False`.
2929

30+
Optionally, you can override the default component creation behavior by implementing the `create` class method. Most components won't need this when props are straightforward conversions from Python to JavaScript. However, this is useful when you need to add custom initialization logic, transform props, or handle special cases when the component is created.
31+
3032
```md alert warning
3133
# When setting the `library` attribute, it is recommended to included a pinned version of the package. Doing so, the package will only change when you intentionally update the version, avoid unexpected breaking changes.
3234
```
@@ -49,6 +51,21 @@ class MyBaseComponent(rx.Component):
4951

5052
# If the component is a default export from the module, set this to True.
5153
is_default = True/False
54+
55+
@classmethod
56+
def create(cls, *children, **props):
57+
"""Create an instance of MyBaseComponent.
58+
59+
Args:
60+
*children: The children of the component.
61+
**props: The props of the component.
62+
63+
Returns:
64+
The component instance.
65+
"""
66+
# Your custom creation logic here
67+
return super().create(*children, **props)
68+
5269
```
5370

5471
# Wrapping a Dynamic Component
@@ -95,3 +112,55 @@ The reason for this is that it does not make sense for your server to render the
95112

96113
In addition, if in the component documentation it mentions nextJS compatibility or server side rendering compatibility, it is a good sign that it requires dynamic imports.
97114

115+
# Advanced - Parsing a state Var with a JS Function
116+
When wrapping a component, you may need to parse a state var by applying a JS function to it.
117+
118+
## Define the parsing function
119+
120+
First you need to define the parsing function by writing it in `add_custom_code`.
121+
122+
```python
123+
124+
def add_custom_code(self) -> list[str]:
125+
"""Add custom code to the component."""
126+
# Define the parsing function
127+
return [
128+
"""
129+
function myParsingFunction(inputProp) {
130+
// Your parsing logic here
131+
return parsedProp;
132+
}"""
133+
]
134+
```
135+
136+
## Apply the parsing function to your props
137+
138+
Then, you can apply the parsing function to your props in the `create` method.
139+
140+
```python
141+
from reflex.vars.base import Var
142+
from reflex.vars.function import FunctionStringVar
143+
144+
...
145+
@classmethod
146+
def create(cls, *children, **props):
147+
"""Create an instance of MyBaseComponent.
148+
149+
Args:
150+
*children: The children of the component.
151+
**props: The props of the component.
152+
153+
Returns:
154+
The component instance.
155+
"""
156+
# Apply the parsing function to the props
157+
if (prop_to_parse := props.get("propsToParse")) is not None:
158+
if isinstance(prop_to_parse, Var):
159+
props["propsToParse"] = FunctionStringVar.create("myParsingFunction").call(prop_to_parse)
160+
else:
161+
# This is not a state Var, so you can parse the value directly in python
162+
parsed_prop = python_parsing_function(prop_to_parse)
163+
props["propsToParse"] = parsed_prop
164+
return super().create(*children, **props)
165+
...
166+
```

docs/wrapping-react/props.md

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Broadly, there are three kinds of props you can encounter when wrapping a React
1717

1818
Simple props are the most common type of props you will encounter when wrapping a React component. They are passed directly to the component and can be of any type (but most commonly strings, numbers, booleans, and structures).
1919

20+
For custom types, you can use `TypedDict` to define the structure of the custom types. However, if you need the attributes to be automatically converted to camelCase once compiled in JS, you can use `rx.PropsBase` instead of `TypedDict`.
21+
2022
```python
2123
class CustomReactType(TypedDict):
2224
"""Custom React type."""
@@ -26,6 +28,15 @@ class CustomReactType(TypedDict):
2628
attribute2: bool
2729
attribute3: int
2830

31+
32+
class CustomReactType2(rx.PropsBase):
33+
"""Custom React type."""
34+
35+
# Define the structure of the custom type to match the Javascript structure.
36+
attr_foo: str # will be attrFoo in JS
37+
attr_bar: bool # will be attrBar in JS
38+
attr_baz: int # will be attrBaz in JS
39+
2940
class SimplePropsComponent(MyBaseComponent):
3041
"""MyComponent."""
3142

@@ -46,9 +57,12 @@ class SimplePropsComponent(MyBaseComponent):
4657
# props annotated as `CustomReactType` in javascript
4758
props5: rx.Var[CustomReactType]
4859

60+
# props annotated as `CustomReactType2` in javascript
61+
props6: rx.Var[CustomReactType2]
62+
4963
# Sometimes a props will accept multiple types. You can use `|` to specify the types.
5064
# props annotated as `string | boolean` in javascript
51-
props6: rx.Var[str | bool]
65+
props7: rx.Var[str | bool]
5266
```
5367

5468
## Callback Props
@@ -92,14 +106,30 @@ class InputEventType(TypedDict):
92106
foo: str
93107
bar: int
94108

109+
class OutputEventType(TypedDict):
110+
"""Output event type."""
111+
112+
# Define the structure of the output event.
113+
baz: str
114+
qux: int
115+
95116

96-
def custom_spec(event: ObjectVar[InputEventType]) -> tuple[str, int]:
97-
"""Custom event spec."""
117+
def custom_spec1(event: ObjectVar[InputEventType]) -> tuple[str, int]:
118+
"""Custom event spec using ObjectVar with custom type as input and tuple as output."""
98119
return (
99120
event.foo.to(str),
100121
event.bar.to(int),
101122
)
102123

124+
def custom_spec2(event: ObjectVar[dict]) -> tuple[Var[OutputEventType]]:
125+
"""Custom event spec using ObjectVar with dict as input and custom type as output."""
126+
return Var.create(
127+
{
128+
"baz": event["foo"],
129+
"qux": event["bar"],
130+
},
131+
).to(OutputEventType)
132+
103133
class EventHandlerComponent(MyBaseComponent):
104134
"""MyComponent."""
105135

@@ -115,8 +145,11 @@ class EventHandlerComponent(MyBaseComponent):
115145
# An event handler specialized for key events, accessing event.key from the event and provided modifiers (ctrl, alt, shift, meta).
116146
on_key_down: rx.EventHandler[rx.event.key_event]
117147

118-
# An event handler that takes a custom spec.
119-
on_custom_event: rx.EventHandler[custom_spec]
148+
# An event handler that takes a custom spec. (Event handler must expect a tuple of two values [str and int])
149+
on_custom_event: rx.EventHandler[custom_spec1]
150+
151+
# Another event handler that takes a custom spec. (Event handler must expect a tuple of one value, being a OutputEventType)
152+
on_custom_event2: rx.EventHandler[custom_spec2]
120153
```
121154

122155
```md alert info

0 commit comments

Comments
 (0)