or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

backend.mdcomponents.mdconfiguration.mdevents.mdhooks.mdhtml-elements.mdindex.mdsvg-elements.mdtesting.mdvdom.mdweb-modules.mdwidgets.md

hooks.mddocs/

0

# Hooks and State Management

1

2

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.

3

4

## Capabilities

5

6

### State Hook

7

8

The `use_state` hook manages component state with a getter and setter pattern.

9

10

```python { .api }

11

def use_state(initial_value: T | Callable[[], T]) -> tuple[T, Callable[[T], None]]: ...

12

```

13

14

**Parameters:**

15

- `initial_value`: Initial state value or a function that returns the initial value

16

17

**Returns:** Tuple of `(current_value, setter_function)`

18

19

**Usage Examples:**

20

21

```python

22

from reactpy import component, html, use_state

23

24

@component

25

def Counter():

26

count, set_count = use_state(0)

27

28

def increment():

29

set_count(count + 1)

30

31

def decrement():

32

set_count(count - 1)

33

34

return html.div(

35

html.h1(f"Count: {count}"),

36

html.button({"onClick": increment}, "Increment"),

37

html.button({"onClick": decrement}, "Decrement")

38

)

39

40

@component

41

def LazyInitialization():

42

# Use function for expensive initial computation

43

expensive_value, set_value = use_state(lambda: expensive_computation())

44

return html.div(f"Value: {expensive_value}")

45

```

46

47

### Effect Hook

48

49

The `use_effect` hook handles side effects with optional dependency tracking.

50

51

```python { .api }

52

def use_effect(function: Callable[[], None | Callable[[], None]], dependencies: list[Any] | None = None) -> None: ...

53

```

54

55

**Parameters:**

56

- `function`: Effect function, optionally returns cleanup function

57

- `dependencies`: List of values that trigger re-execution when changed (None = every render, [] = once)

58

59

**Usage Examples:**

60

61

```python

62

@component

63

def Timer():

64

seconds, set_seconds = use_state(0)

65

66

def setup_timer():

67

interval_id = set_interval(lambda: set_seconds(seconds + 1), 1000)

68

return lambda: clear_interval(interval_id) # Cleanup function

69

70

use_effect(setup_timer, []) # Run once on mount

71

72

return html.div(f"Timer: {seconds} seconds")

73

74

@component

75

def DocumentTitle(title: str):

76

def update_title():

77

document.title = title

78

79

use_effect(update_title, [title]) # Run when title changes

80

81

return html.h1(title)

82

```

83

84

### Callback Hook

85

86

The `use_callback` hook memoizes callback functions to prevent unnecessary re-renders.

87

88

```python { .api }

89

def use_callback(function: Callable[..., Any], dependencies: list[Any] | None = None) -> Callable[..., Any]: ...

90

```

91

92

**Parameters:**

93

- `function`: Function to memoize

94

- `dependencies`: List of values that invalidate the memoized callback

95

96

**Usage Examples:**

97

98

```python

99

@component

100

def TodoList():

101

todos, set_todos = use_state([])

102

103

add_todo = use_callback(

104

lambda text: set_todos([*todos, {"id": len(todos), "text": text}]),

105

[todos]

106

)

107

108

return html.div(

109

TodoForm({"onSubmit": add_todo}),

110

*[TodoItem(todo, key=todo["id"]) for todo in todos]

111

)

112

```

113

114

### Memo Hook

115

116

The `use_memo` hook memoizes expensive computations.

117

118

```python { .api }

119

def use_memo(function: Callable[[], T], dependencies: list[Any] | None = None) -> T: ...

120

```

121

122

**Parameters:**

123

- `function`: Function that computes the value

124

- `dependencies`: List of values that invalidate the memoized result

125

126

**Usage Examples:**

127

128

```python

129

@component

130

def ExpensiveCalculation(numbers: list[int]):

131

expensive_result = use_memo(

132

lambda: sum(x**2 for x in numbers),

133

[numbers]

134

)

135

136

return html.div(f"Sum of squares: {expensive_result}")

137

```

138

139

### Ref Hook

140

141

The `use_ref` hook creates a mutable reference that persists across renders.

142

143

```python { .api }

144

def use_ref(initial_value: T = None) -> Ref[T]: ...

145

146

class Ref[T]:

147

current: T

148

def __init__(self, initial_value: T = None): ...

149

```

150

151

**Returns:** Ref object with `current` attribute

152

153

**Usage Examples:**

154

155

```python

156

@component

157

def FocusInput():

158

input_ref = use_ref(None)

159

160

def focus_input():

161

if input_ref.current:

162

input_ref.current.focus()

163

164

return html.div(

165

html.input({"ref": input_ref}),

166

html.button({"onClick": focus_input}, "Focus Input")

167

)

168

169

@component

170

def PreviousValue(value: int):

171

previous_ref = use_ref(None)

172

173

use_effect(lambda: setattr(previous_ref, 'current', value))

174

175

return html.div(f"Current: {value}, Previous: {previous_ref.current}")

176

```

177

178

### Reducer Hook

179

180

The `use_reducer` hook manages complex state with reducer pattern.

181

182

```python { .api }

183

def use_reducer(

184

reducer: Callable[[T, Any], T],

185

initial_value: T,

186

init: Callable[[T], T] | None = None

187

) -> tuple[T, Callable[[Any], None]]: ...

188

```

189

190

**Parameters:**

191

- `reducer`: Function that takes (state, action) and returns new state

192

- `initial_value`: Initial state value

193

- `init`: Optional function to compute initial state lazily

194

195

**Returns:** Tuple of `(current_state, dispatch_function)`

196

197

**Usage Examples:**

198

199

```python

200

def counter_reducer(state, action):

201

if action["type"] == "increment":

202

return state + 1

203

elif action["type"] == "decrement":

204

return state - 1

205

elif action["type"] == "reset":

206

return 0

207

return state

208

209

@component

210

def CounterWithReducer():

211

count, dispatch = use_reducer(counter_reducer, 0)

212

213

return html.div(

214

html.h1(f"Count: {count}"),

215

html.button({"onClick": lambda: dispatch({"type": "increment"})}, "Increment"),

216

html.button({"onClick": lambda: dispatch({"type": "decrement"})}, "Decrement"),

217

html.button({"onClick": lambda: dispatch({"type": "reset"})}, "Reset")

218

)

219

```

220

221

### Context Hooks

222

223

Context hooks enable data sharing across component trees without prop drilling.

224

225

```python { .api }

226

def create_context(default_value: T = None) -> Context[T]: ...

227

def use_context(context: Context[T]) -> T: ...

228

```

229

230

**Usage Examples:**

231

232

```python

233

# Create context

234

ThemeContext = create_context("light")

235

236

@component

237

def ThemeProvider(*children, theme: str = "light"):

238

return ThemeContext.Provider({"value": theme}, *children)

239

240

@component

241

def ThemedButton():

242

theme = use_context(ThemeContext)

243

244

return html.button(

245

{"className": f"btn btn-{theme}"},

246

"Themed Button"

247

)

248

```

249

250

### Specialized Hooks

251

252

Additional hooks for specific use cases:

253

254

```python { .api }

255

def use_connection() -> Connection: ...

256

def use_location() -> Location: ...

257

def use_debug_value(value: Any, formatter: Callable[[Any], str] | None = None) -> None: ...

258

def use_scope() -> dict[str, Any]: ...

259

def use_exception() -> Callable[[Exception], None]: ...

260

```

261

262

**Usage Examples:**

263

264

```python

265

@component

266

def ConnectionInfo():

267

connection = use_connection()

268

return html.div(f"Connected from: {connection.get('remote_addr')}")

269

270

@component

271

def LocationAware():

272

location = use_location()

273

return html.div(f"Current path: {location.get('pathname')}")

274

275

@component

276

def DebuggableComponent():

277

count, set_count = use_state(0)

278

use_debug_value(count, lambda c: f"Count: {c}")

279

280

return html.div(f"Count: {count}")

281

```

282

283

### Hook Rules

284

285

1. **Only call hooks from component functions** - Never call hooks from regular functions, loops, or conditionals

286

2. **Call hooks in the same order** - Don't call hooks inside loops, conditions, or nested functions

287

3. **Hook dependencies** - Always include all values used inside effects in the dependency array

288

289

```python

290

# ❌ Wrong - conditional hook

291

@component

292

def BadComponent(condition: bool):

293

if condition:

294

count, set_count = use_state(0) # Don't do this

295

return html.div()

296

297

# ✅ Correct - hooks at top level

298

@component

299

def GoodComponent(condition: bool):

300

count, set_count = use_state(0)

301

302

if condition:

303

return html.div(f"Count: {count}")

304

return html.div("No count")

305

```