or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

composition-lifecycle.mdcomposition-local.mdeffects.mdindex.mdios-integration.mdsnapshot-system.mdstate-management.md

effects.mddocs/

0

# Effects

1

2

Side effect management with lifecycle-aware execution and automatic cleanup. Effects provide safe ways to perform operations outside the composition scope while integrating properly with the Compose lifecycle.

3

4

## Capabilities

5

6

### DisposableEffect

7

8

Effect that requires cleanup when the effect leaves the composition or its keys change.

9

10

```kotlin { .api }

11

/**

12

* Effect that runs when keys change and provides cleanup via DisposableEffectResult

13

* @param key1 Key that determines when effect should restart

14

* @param effect Lambda that runs the effect and returns cleanup

15

*/

16

@Composable

17

fun DisposableEffect(

18

key1: Any?,

19

effect: DisposableEffectScope.() -> DisposableEffectResult

20

): Unit

21

22

/**

23

* DisposableEffect with two key dependencies

24

* @param key1 First key dependency

25

* @param key2 Second key dependency

26

* @param effect Lambda that runs the effect and returns cleanup

27

*/

28

@Composable

29

fun DisposableEffect(

30

key1: Any?,

31

key2: Any?,

32

effect: DisposableEffectScope.() -> DisposableEffectResult

33

): Unit

34

35

/**

36

* DisposableEffect with three key dependencies

37

* @param key1 First key dependency

38

* @param key2 Second key dependency

39

* @param key3 Third key dependency

40

* @param effect Lambda that runs the effect and returns cleanup

41

*/

42

@Composable

43

fun DisposableEffect(

44

key1: Any?,

45

key2: Any?,

46

key3: Any?,

47

effect: DisposableEffectScope.() -> DisposableEffectResult

48

): Unit

49

50

/**

51

* DisposableEffect with multiple key dependencies

52

* @param keys Variable number of key dependencies

53

* @param effect Lambda that runs the effect and returns cleanup

54

*/

55

@Composable

56

fun DisposableEffect(

57

vararg keys: Any?,

58

effect: DisposableEffectScope.() -> DisposableEffectResult

59

): Unit

60

61

/**

62

* Scope for DisposableEffect providing cleanup mechanism

63

*/

64

interface DisposableEffectScope {

65

/**

66

* Creates a cleanup handler

67

* @param onDispose Function called when effect is disposed

68

* @return DisposableEffectResult for the effect

69

*/

70

fun onDispose(onDispose: () -> Unit): DisposableEffectResult

71

}

72

73

/**

74

* Result of a DisposableEffect containing cleanup logic

75

*/

76

interface DisposableEffectResult

77

```

78

79

**Usage Examples:**

80

81

```kotlin

82

@Composable

83

fun DisposableEffectExample(userId: String) {

84

// Effect that depends on userId

85

DisposableEffect(userId) {

86

val subscription = userService.subscribeToUser(userId) { user ->

87

// Handle user updates

88

}

89

90

// Cleanup when userId changes or composable leaves composition

91

onDispose {

92

subscription.cancel()

93

}

94

}

95

96

// iOS-specific example: Observer pattern

97

DisposableEffect(Unit) {

98

val observer = NotificationCenter.default.addObserver(

99

forName: UIApplication.didBecomeActiveNotification,

100

object: null,

101

queue: OperationQueue.main

102

) { _ ->

103

// Handle app becoming active

104

}

105

106

onDispose {

107

NotificationCenter.default.removeObserver(observer)

108

}

109

}

110

}

111

```

112

113

### LaunchedEffect

114

115

Effect that launches a coroutine and automatically cancels it when the effect leaves the composition.

116

117

```kotlin { .api }

118

/**

119

* Launches a coroutine in the composition scope

120

* @param key1 Key that determines when to restart the coroutine

121

* @param block Suspending function to execute in the coroutine

122

*/

123

@Composable

124

fun LaunchedEffect(

125

key1: Any?,

126

block: suspend CoroutineScope.() -> Unit

127

): Unit

128

129

/**

130

* LaunchedEffect with two key dependencies

131

* @param key1 First key dependency

132

* @param key2 Second key dependency

133

* @param block Suspending function to execute in the coroutine

134

*/

135

@Composable

136

fun LaunchedEffect(

137

key1: Any?,

138

key2: Any?,

139

block: suspend CoroutineScope.() -> Unit

140

): Unit

141

142

/**

143

* LaunchedEffect with three key dependencies

144

* @param key1 First key dependency

145

* @param key2 Second key dependency

146

* @param key3 Third key dependency

147

* @param block Suspending function to execute in the coroutine

148

*/

149

@Composable

150

fun LaunchedEffect(

151

key1: Any?,

152

key2: Any?,

153

key3: Any?,

154

block: suspend CoroutineScope.() -> Unit

155

): Unit

156

157

/**

158

* LaunchedEffect with multiple key dependencies

159

* @param keys Variable number of key dependencies

160

* @param block Suspending function to execute in the coroutine

161

*/

162

@Composable

163

fun LaunchedEffect(

164

vararg keys: Any?,

165

block: suspend CoroutineScope.() -> Unit

166

): Unit

167

```

168

169

**Usage Examples:**

170

171

```kotlin

172

@Composable

173

fun LaunchedEffectExample(searchQuery: String) {

174

var searchResults by remember { mutableStateOf<List<SearchResult>>(emptyList()) }

175

176

// Launch search when query changes

177

LaunchedEffect(searchQuery) {

178

if (searchQuery.isNotEmpty()) {

179

delay(300) // Debounce

180

try {

181

searchResults = searchService.search(searchQuery)

182

} catch (e: Exception) {

183

// Handle error

184

}

185

}

186

}

187

188

// Launch periodic update

189

LaunchedEffect(Unit) {

190

while (true) {

191

delay(30_000) // 30 seconds

192

refreshData()

193

}

194

}

195

}

196

197

@Composable

198

fun NetworkStatusExample() {

199

var isOnline by remember { mutableStateOf(true) }

200

201

LaunchedEffect(Unit) {

202

networkMonitor.status.collect { status ->

203

isOnline = status == NetworkStatus.Connected

204

}

205

}

206

}

207

```

208

209

### SideEffect

210

211

Effect that executes after every successful recomposition.

212

213

```kotlin { .api }

214

/**

215

* Executes a side effect after every recomposition

216

* @param effect Function to execute after recomposition

217

*/

218

@Composable

219

fun SideEffect(effect: () -> Unit): Unit

220

```

221

222

**Usage Examples:**

223

224

```kotlin

225

@Composable

226

fun SideEffectExample(user: User) {

227

// Log every time this composable recomposes

228

SideEffect {

229

logger.log("UserProfile recomposed for user: ${user.id}")

230

}

231

232

// Update analytics after recomposition

233

SideEffect {

234

analytics.track("profile_viewed", mapOf("user_id" to user.id))

235

}

236

}

237

238

@Composable

239

fun UIKitIntegrationExample() {

240

val currentUser by viewModel.currentUser.collectAsState()

241

242

// Update UIKit view after recomposition

243

SideEffect {

244

navigationController.title = currentUser?.name ?: "Profile"

245

}

246

}

247

```

248

249

### rememberCoroutineScope

250

251

Returns a CoroutineScope that's automatically cancelled when the composition leaves.

252

253

```kotlin { .api }

254

/**

255

* Returns a CoroutineScope bound to the composition lifecycle

256

* @return CoroutineScope that's cancelled when composition is disposed

257

*/

258

@Composable

259

fun rememberCoroutineScope(): CoroutineScope

260

261

/**

262

* Returns a CoroutineScope bound to the composition lifecycle with custom context

263

* @param getContext Function to provide custom coroutine context

264

* @return CoroutineScope with custom context that's cancelled when composition is disposed

265

*/

266

@Composable

267

fun rememberCoroutineScope(getContext: () -> CoroutineContext): CoroutineScope

268

```

269

270

**Usage Examples:**

271

272

```kotlin

273

@Composable

274

fun CoroutineScopeExample() {

275

val scope = rememberCoroutineScope()

276

val snackbarHostState = remember { SnackbarHostState() }

277

278

Column {

279

Button(onClick = {

280

// Launch coroutine in response to user action

281

scope.launch {

282

try {

283

val result = performNetworkCall()

284

snackbarHostState.showSnackbar("Success: $result")

285

} catch (e: Exception) {

286

snackbarHostState.showSnackbar("Error: ${e.message}")

287

}

288

}

289

}) {

290

Text("Perform Action")

291

}

292

293

SnackbarHost(hostState = snackbarHostState)

294

}

295

}

296

297

@Composable

298

fun CustomCoroutineScopeExample() {

299

// Custom scope with IO dispatcher for background work

300

val ioScope = rememberCoroutineScope { Dispatchers.IO }

301

302

// Custom scope for iOS main thread operations

303

val mainScope = rememberCoroutineScope { IOSMainDispatcher }

304

305

Button(onClick = {

306

// Heavy computation on IO dispatcher

307

ioScope.launch {

308

val data = performHeavyComputation()

309

310

// Switch to main thread for UI updates

311

mainScope.launch {

312

updateUI(data)

313

}

314

}

315

}) {

316

Text("Process Data")

317

}

318

}

319

```

320

321

### rememberUpdatedState

322

323

Remembers a value that's always up-to-date in long-running effects.

324

325

```kotlin { .api }

326

/**

327

* Remembers a value that's always current in long-running operations

328

* @param newValue The current value to remember

329

* @return State containing the most recent value

330

*/

331

@Composable

332

fun <T> rememberUpdatedState(newValue: T): State<T>

333

```

334

335

**Usage Examples:**

336

337

```kotlin

338

@Composable

339

fun TimerExample(onTimeout: () -> Unit) {

340

// Remember the latest callback to avoid stale captures

341

val currentOnTimeout by rememberUpdatedState(onTimeout)

342

343

LaunchedEffect(Unit) {

344

delay(5000) // 5 second timer

345

currentOnTimeout() // This will call the latest onTimeout

346

}

347

}

348

349

@Composable

350

fun IntervalExample(interval: Long, action: () -> Unit) {

351

val currentAction by rememberUpdatedState(action)

352

353

LaunchedEffect(interval) {

354

while (true) {

355

delay(interval)

356

currentAction() // Always uses the latest action

357

}

358

}

359

}

360

```

361

362

## Advanced Effect Patterns

363

364

### Effect Error Handling

365

366

```kotlin

367

@Composable

368

fun ErrorHandlingExample() {

369

var error by remember { mutableStateOf<String?>(null) }

370

371

LaunchedEffect(Unit) {

372

try {

373

performRiskyOperation()

374

} catch (e: Exception) {

375

error = e.message

376

}

377

}

378

379

error?.let { errorMessage ->

380

Text("Error: $errorMessage", color = Color.Red)

381

}

382

}

383

```

384

385

### Effect Cancellation

386

387

```kotlin

388

@Composable

389

fun CancellationExample(shouldRun: Boolean) {

390

LaunchedEffect(shouldRun) {

391

if (shouldRun) {

392

try {

393

while (true) {

394

performPeriodicTask()

395

delay(1000)

396

}

397

} catch (e: CancellationException) {

398

// Cleanup on cancellation

399

cleanup()

400

throw e // Re-throw to properly handle cancellation

401

}

402

}

403

}

404

}

405

```

406

407

### Complex Effect Dependencies

408

409

```kotlin

410

@Composable

411

fun ComplexDependenciesExample(

412

userId: String,

413

refreshTrigger: Int,

414

settings: UserSettings

415

) {

416

// Effect runs when any dependency changes

417

LaunchedEffect(userId, refreshTrigger, settings.theme) {

418

loadUserData(userId, settings)

419

}

420

}

421

```

422

423

## iOS Platform Considerations

424

425

### Main Thread Integration

426

427

```kotlin

428

@Composable

429

fun IOSMainThreadExample() {

430

LaunchedEffect(Unit) {

431

// Switch to main dispatcher for UI updates

432

withContext(Dispatchers.Main.immediate) {

433

updateUIKitView()

434

}

435

}

436

}

437

```

438

439

### Background Task Management

440

441

```kotlin

442

@Composable

443

fun BackgroundTaskExample() {

444

DisposableEffect(Unit) {

445

val taskId = UIApplication.shared.beginBackgroundTask {

446

// Background task expired

447

}

448

449

onDispose {

450

UIApplication.shared.endBackgroundTask(taskId)

451

}

452

}

453

}

454

```

455

456

## Performance Considerations

457

458

- **Effect Keys**: Use appropriate keys to control when effects restart

459

- **Cancellation**: Always handle coroutine cancellation properly in LaunchedEffect

460

- **Resource Cleanup**: Use DisposableEffect for resources that need explicit cleanup

461

- **State Updates**: Use rememberUpdatedState for callbacks in long-running effects

462

- **Thread Safety**: Be mindful of thread safety when accessing mutable state from effects