or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app.mdexpress.mdindex.mdreactive.mdrender.mdsession.mdtypes.mdui.md

reactive.mddocs/

0

# Reactive Programming

1

2

Shiny's reactive programming system automatically tracks dependencies and updates outputs when inputs change. The reactive system includes reactive values, calculations, effects, and event handling that form the core of Shiny's reactivity model.

3

4

## Capabilities

5

6

### Reactive Values

7

8

Reactive values are the foundation of Shiny's reactive system, storing data that can trigger updates when changed.

9

10

```python { .api }

11

def reactive.value(value: T) -> Value[T]:

12

"""

13

Create a reactive value.

14

15

Args:

16

value: Initial value of any type.

17

18

Returns:

19

Value object that can be read and written reactively.

20

"""

21

22

class reactive.Value[T]:

23

"""

24

A reactive value that can be read and written.

25

"""

26

def __call__(self) -> T:

27

"""Get the current value."""

28

29

def set(self, value: T) -> None:

30

"""Set a new value, triggering reactive updates."""

31

32

def freeze(self) -> T:

33

"""Get value without establishing reactive dependency."""

34

35

def is_set(self) -> bool:

36

"""Check if value has been set."""

37

```

38

39

#### Usage Examples

40

41

```python

42

from shiny import reactive

43

44

# Create reactive values

45

counter = reactive.value(0)

46

user_name = reactive.value("")

47

selected_data = reactive.value(None)

48

49

# Reading reactive values (establishes dependency)

50

current_count = counter() # Will trigger updates when counter changes

51

name = user_name() # Will trigger updates when user_name changes

52

53

# Setting reactive values (triggers dependent updates)

54

counter.set(counter() + 1) # Increment counter

55

user_name.set("Alice") # Update name

56

selected_data.set(df.iloc[0:10]) # Update data selection

57

58

# Non-reactive reading (no dependency established)

59

current_count_frozen = counter.freeze()

60

61

# Check if value has been set

62

if selected_data.is_set():

63

process_data(selected_data())

64

```

65

66

### Reactive Calculations

67

68

Reactive calculations are cached computations that automatically update when their dependencies change.

69

70

```python { .api }

71

def reactive.calc(fn: Callable[[], T]) -> Calc[T]:

72

"""

73

Create a reactive calculation.

74

75

Args:

76

fn: Function that computes the value. Should not have side effects.

77

78

Returns:

79

Calc object that caches the computed value.

80

"""

81

82

class reactive.Calc[T]:

83

"""

84

A reactive calculation that caches computed values.

85

"""

86

def __call__(self) -> T:

87

"""Get the computed value, recalculating if dependencies changed."""

88

89

def invalidate(self) -> None:

90

"""Invalidate the cached value, forcing recalculation."""

91

```

92

93

#### Usage Examples

94

95

```python

96

# Create reactive calculations

97

@reactive.calc

98

def filtered_data():

99

df = input.dataset()

100

filter_val = input.filter_value()

101

return df[df['column'] > filter_val] # Automatically updates when inputs change

102

103

@reactive.calc

104

def summary_stats():

105

data = filtered_data() # Depends on filtered_data calculation

106

return {

107

'mean': data['value'].mean(),

108

'std': data['value'].std(),

109

'count': len(data)

110

}

111

112

# Use calculations in outputs

113

@output

114

@render.text

115

def stats_display():

116

stats = summary_stats() # Will update when underlying data changes

117

return f"Mean: {stats['mean']:.2f}, Count: {stats['count']}"

118

119

# Alternative syntax without decorator

120

processed_data = reactive.calc(lambda: expensive_computation(input.data()))

121

```

122

123

### Reactive Effects

124

125

Reactive effects perform side effects when their dependencies change, but don't return values.

126

127

```python { .api }

128

def reactive.effect(fn: Callable[[], None]) -> Effect:

129

"""

130

Create a reactive effect.

131

132

Args:

133

fn: Function that performs side effects. Should not return a value.

134

135

Returns:

136

Effect object for managing the effect.

137

"""

138

139

class reactive.Effect:

140

"""

141

A reactive effect that performs side effects.

142

"""

143

def destroy(self) -> None:

144

"""Stop the effect from running."""

145

146

def invalidate(self) -> None:

147

"""Invalidate the effect, causing it to re-run."""

148

```

149

150

#### Usage Examples

151

152

```python

153

# Create reactive effects

154

@reactive.effect

155

def update_database():

156

# Runs whenever selected_data changes

157

data = selected_data()

158

if data is not None:

159

database.save_selection(data)

160

161

@reactive.effect

162

def log_user_activity():

163

# Log when user changes inputs

164

current_page = input.page()

165

user_id = session.get_user_id()

166

logger.info(f"User {user_id} viewed page {current_page}")

167

168

# Manual effect creation

169

def cleanup_temp_files():

170

temp_dir = input.temp_directory()

171

if temp_dir and os.path.exists(temp_dir):

172

shutil.rmtree(temp_dir)

173

174

cleanup_effect = reactive.effect(cleanup_temp_files)

175

176

# Destroy effect when no longer needed

177

# cleanup_effect.destroy()

178

```

179

180

### Event Handling

181

182

Event-based reactivity for controlling when reactive code should run.

183

184

```python { .api }

185

def reactive.event(

186

*args: object,

187

ignore_none: bool = True,

188

ignore_init: bool = False

189

) -> Callable[[Callable[[], T]], Callable[[], T]]:

190

"""

191

Decorator for event-driven reactivity.

192

193

Args:

194

*args: Event sources (typically input values or reactive values).

195

ignore_none: Don't trigger on None values.

196

ignore_init: Don't trigger on initial evaluation.

197

198

Returns:

199

Decorator function.

200

"""

201

```

202

203

#### Usage Examples

204

205

```python

206

# Event-driven calculations

207

@reactive.calc

208

@reactive.event(input.update_button)

209

def expensive_analysis():

210

# Only runs when update button is clicked, not when data changes

211

return perform_complex_analysis(input.dataset())

212

213

@output

214

@render.plot

215

@reactive.event(input.plot_button, ignore_init=True)

216

def generate_plot():

217

# Only generates plot when button is clicked

218

data = input.data()

219

return create_visualization(data)

220

221

# Multiple event sources

222

@reactive.calc

223

@reactive.event(input.refresh_btn, input.auto_refresh)

224

def updated_data():

225

# Runs when either refresh button clicked OR auto_refresh changes

226

return fetch_latest_data()

227

228

# Event-driven effects

229

@reactive.effect

230

@reactive.event(input.save_button)

231

def save_data():

232

# Save only when save button is clicked

233

current_data = get_current_state()

234

database.save(current_data)

235

```

236

237

### Extended Tasks

238

239

Long-running asynchronous tasks that can be monitored and controlled.

240

241

```python { .api }

242

class reactive.ExtendedTask:

243

"""

244

A task that can run for an extended period with status tracking.

245

"""

246

def __init__(self, func: Callable[..., Awaitable[T]]): ...

247

248

def __call__(self, *args: object, **kwargs: object) -> ExtendedTask[T]: ...

249

250

def cancel(self) -> None:

251

"""Cancel the running task."""

252

253

def result(self) -> T:

254

"""Get the task result (blocks until complete)."""

255

256

def status(self) -> Literal["initial", "running", "success", "error", "cancelled"]:

257

"""Get current task status."""

258

259

def reactive.extended_task(

260

func: Callable[..., Awaitable[T]]

261

) -> ExtendedTask[T]:

262

"""

263

Decorator to create an extended task.

264

265

Args:

266

func: Async function to run as extended task.

267

268

Returns:

269

ExtendedTask wrapper.

270

"""

271

```

272

273

#### Usage Examples

274

275

```python

276

import asyncio

277

from shiny import reactive

278

279

# Define extended task

280

@reactive.extended_task

281

async def process_large_dataset(dataset_path, parameters):

282

# Long-running data processing

283

await asyncio.sleep(1) # Simulate work

284

285

with open(dataset_path, 'r') as f:

286

data = load_and_process(f, parameters)

287

288

# More processing...

289

await asyncio.sleep(2)

290

291

return analysis_results

292

293

# Use in server function

294

def server(input: Inputs, output: Outputs, session: Session):

295

296

@reactive.effect

297

@reactive.event(input.start_processing)

298

def start_task():

299

# Start the extended task

300

process_large_dataset(

301

input.dataset_file(),

302

input.processing_params()

303

)

304

305

@output

306

@render.text

307

def task_status():

308

# Show current status

309

status = process_large_dataset.status()

310

if status == "running":

311

return "Processing... Please wait."

312

elif status == "success":

313

return "Processing complete!"

314

elif status == "error":

315

return "An error occurred during processing."

316

else:

317

return "Ready to start processing."

318

319

@output

320

@render.table

321

def results():

322

# Show results when task completes

323

if process_large_dataset.status() == "success":

324

return process_large_dataset.result()

325

return None

326

327

@reactive.effect

328

@reactive.event(input.cancel_button)

329

def cancel_task():

330

process_large_dataset.cancel()

331

```

332

333

### Time-Based Reactivity

334

335

Utilities for time-based reactive updates and polling.

336

337

```python { .api }

338

def reactive.invalidate_later(

339

delay: float,

340

session: Session | None = None

341

) -> None:

342

"""

343

Schedule reactive invalidation after a delay.

344

345

Args:

346

delay: Delay in seconds.

347

session: Session to use (current session if None).

348

"""

349

350

def reactive.poll(

351

func: Callable[[], T],

352

interval_secs: float,

353

session: Session | None = None

354

) -> Callable[[], T]:

355

"""

356

Poll a function at regular intervals.

357

358

Args:

359

func: Function to poll.

360

interval_secs: Polling interval in seconds.

361

session: Session to use (current session if None).

362

363

Returns:

364

Reactive function that returns polled value.

365

"""

366

367

def reactive.file_reader(

368

filepath: str | os.PathLike[str],

369

session: Session | None = None

370

) -> Callable[[], str | None]:

371

"""

372

Reactively read a file when it changes.

373

374

Args:

375

filepath: Path to file to monitor.

376

session: Session to use (current session if None).

377

378

Returns:

379

Function that returns file contents when changed.

380

"""

381

```

382

383

#### Usage Examples

384

385

```python

386

# Auto-refresh data every 30 seconds

387

@reactive.calc

388

def live_data():

389

reactive.invalidate_later(30) # Refresh every 30 seconds

390

return fetch_current_data()

391

392

# Poll external API

393

api_data = reactive.poll(

394

lambda: requests.get('https://api.example.com/data').json(),

395

interval_secs=60 # Poll every minute

396

)

397

398

@output

399

@render.text

400

def current_api_data():

401

data = api_data()

402

return f"Latest data: {data}"

403

404

# Monitor log file

405

log_contents = reactive.file_reader("/var/log/myapp.log")

406

407

@output

408

@render.text

409

def log_display():

410

contents = log_contents()

411

if contents:

412

# Show last 10 lines

413

return "\n".join(contents.strip().split("\n")[-10:])

414

return "No log data"

415

```

416

417

### Reactive Context Management

418

419

Low-level utilities for managing reactive contexts.

420

421

```python { .api }

422

def reactive.isolate(fn: Callable[[], T]) -> T:

423

"""

424

Execute function without establishing reactive dependencies.

425

426

Args:

427

fn: Function to execute in isolation.

428

429

Returns:

430

Function result.

431

"""

432

433

def reactive.flush() -> None:

434

"""

435

Force the reactive system to flush all pending updates.

436

"""

437

438

def reactive.get_current_context() -> Context | None:

439

"""

440

Get the current reactive context.

441

442

Returns:

443

Current Context object or None if not in reactive context.

444

"""

445

446

class reactive.Context:

447

"""

448

Reactive execution context.

449

"""

450

def __enter__(self) -> Context: ...

451

def __exit__(self, *args: object) -> None: ...

452

```

453

454

#### Usage Examples

455

456

```python

457

# Execute code without creating reactive dependencies

458

@reactive.calc

459

def smart_calculation():

460

# This will create a reactive dependency

461

primary_input = input.primary_value()

462

463

# This won't create a reactive dependency

464

config_value = reactive.isolate(lambda: input.config_setting())

465

466

# Calculation only re-runs when primary_value changes,

467

# not when config_setting changes

468

return perform_calculation(primary_input, config_value)

469

470

# Force reactive flush for testing or debugging

471

def update_all_outputs():

472

# Make some changes

473

my_reactive_value.set("new value")

474

another_value.set(42)

475

476

# Force immediate update of all dependent calculations

477

reactive.flush()

478

479

# Now all outputs are guaranteed to be current

480

481

# Work with reactive context

482

context = reactive.get_current_context()

483

if context:

484

# We're in a reactive context

485

print("Currently in reactive execution")

486

```