Reactive user interfaces with pure Python
—
React-style hooks for managing component state, side effects, and context in functional components. Hooks enable stateful logic in ReactPy components and must be called from within component functions.
The use_state hook manages component state with a getter and setter pattern.
def use_state(initial_value: T | Callable[[], T]) -> tuple[T, Callable[[T], None]]: ...Parameters:
initial_value: Initial state value or a function that returns the initial valueReturns: Tuple of (current_value, setter_function)
Usage Examples:
from reactpy import component, html, use_state
@component
def Counter():
count, set_count = use_state(0)
def increment():
set_count(count + 1)
def decrement():
set_count(count - 1)
return html.div(
html.h1(f"Count: {count}"),
html.button({"onClick": increment}, "Increment"),
html.button({"onClick": decrement}, "Decrement")
)
@component
def LazyInitialization():
# Use function for expensive initial computation
expensive_value, set_value = use_state(lambda: expensive_computation())
return html.div(f"Value: {expensive_value}")The use_effect hook handles side effects with optional dependency tracking.
def use_effect(function: Callable[[], None | Callable[[], None]], dependencies: list[Any] | None = None) -> None: ...Parameters:
function: Effect function, optionally returns cleanup functiondependencies: List of values that trigger re-execution when changed (None = every render, [] = once)Usage Examples:
@component
def Timer():
seconds, set_seconds = use_state(0)
def setup_timer():
interval_id = set_interval(lambda: set_seconds(seconds + 1), 1000)
return lambda: clear_interval(interval_id) # Cleanup function
use_effect(setup_timer, []) # Run once on mount
return html.div(f"Timer: {seconds} seconds")
@component
def DocumentTitle(title: str):
def update_title():
document.title = title
use_effect(update_title, [title]) # Run when title changes
return html.h1(title)The use_callback hook memoizes callback functions to prevent unnecessary re-renders.
def use_callback(function: Callable[..., Any], dependencies: list[Any] | None = None) -> Callable[..., Any]: ...Parameters:
function: Function to memoizedependencies: List of values that invalidate the memoized callbackUsage Examples:
@component
def TodoList():
todos, set_todos = use_state([])
add_todo = use_callback(
lambda text: set_todos([*todos, {"id": len(todos), "text": text}]),
[todos]
)
return html.div(
TodoForm({"onSubmit": add_todo}),
*[TodoItem(todo, key=todo["id"]) for todo in todos]
)The use_memo hook memoizes expensive computations.
def use_memo(function: Callable[[], T], dependencies: list[Any] | None = None) -> T: ...Parameters:
function: Function that computes the valuedependencies: List of values that invalidate the memoized resultUsage Examples:
@component
def ExpensiveCalculation(numbers: list[int]):
expensive_result = use_memo(
lambda: sum(x**2 for x in numbers),
[numbers]
)
return html.div(f"Sum of squares: {expensive_result}")The use_ref hook creates a mutable reference that persists across renders.
def use_ref(initial_value: T = None) -> Ref[T]: ...
class Ref[T]:
current: T
def __init__(self, initial_value: T = None): ...Returns: Ref object with current attribute
Usage Examples:
@component
def FocusInput():
input_ref = use_ref(None)
def focus_input():
if input_ref.current:
input_ref.current.focus()
return html.div(
html.input({"ref": input_ref}),
html.button({"onClick": focus_input}, "Focus Input")
)
@component
def PreviousValue(value: int):
previous_ref = use_ref(None)
use_effect(lambda: setattr(previous_ref, 'current', value))
return html.div(f"Current: {value}, Previous: {previous_ref.current}")The use_reducer hook manages complex state with reducer pattern.
def use_reducer(
reducer: Callable[[T, Any], T],
initial_value: T,
init: Callable[[T], T] | None = None
) -> tuple[T, Callable[[Any], None]]: ...Parameters:
reducer: Function that takes (state, action) and returns new stateinitial_value: Initial state valueinit: Optional function to compute initial state lazilyReturns: Tuple of (current_state, dispatch_function)
Usage Examples:
def counter_reducer(state, action):
if action["type"] == "increment":
return state + 1
elif action["type"] == "decrement":
return state - 1
elif action["type"] == "reset":
return 0
return state
@component
def CounterWithReducer():
count, dispatch = use_reducer(counter_reducer, 0)
return html.div(
html.h1(f"Count: {count}"),
html.button({"onClick": lambda: dispatch({"type": "increment"})}, "Increment"),
html.button({"onClick": lambda: dispatch({"type": "decrement"})}, "Decrement"),
html.button({"onClick": lambda: dispatch({"type": "reset"})}, "Reset")
)Context hooks enable data sharing across component trees without prop drilling.
def create_context(default_value: T = None) -> Context[T]: ...
def use_context(context: Context[T]) -> T: ...Usage Examples:
# Create context
ThemeContext = create_context("light")
@component
def ThemeProvider(*children, theme: str = "light"):
return ThemeContext.Provider({"value": theme}, *children)
@component
def ThemedButton():
theme = use_context(ThemeContext)
return html.button(
{"className": f"btn btn-{theme}"},
"Themed Button"
)Additional hooks for specific use cases:
def use_connection() -> Connection: ...
def use_location() -> Location: ...
def use_debug_value(value: Any, formatter: Callable[[Any], str] | None = None) -> None: ...
def use_scope() -> dict[str, Any]: ...
def use_exception() -> Callable[[Exception], None]: ...Usage Examples:
@component
def ConnectionInfo():
connection = use_connection()
return html.div(f"Connected from: {connection.get('remote_addr')}")
@component
def LocationAware():
location = use_location()
return html.div(f"Current path: {location.get('pathname')}")
@component
def DebuggableComponent():
count, set_count = use_state(0)
use_debug_value(count, lambda c: f"Count: {c}")
return html.div(f"Count: {count}")# ❌ Wrong - conditional hook
@component
def BadComponent(condition: bool):
if condition:
count, set_count = use_state(0) # Don't do this
return html.div()
# ✅ Correct - hooks at top level
@component
def GoodComponent(condition: bool):
count, set_count = use_state(0)
if condition:
return html.div(f"Count: {count}")
return html.div("No count")Install with Tessl CLI
npx tessl i tessl/pypi-reactpy