0
# Lifecycle Hooks
1
2
Component lifecycle management hooks for handling mounting, updating, rendering, and cleanup operations.
3
4
## Capabilities
5
6
### onWillStart
7
8
Called before the component starts its initial rendering process.
9
10
```typescript { .api }
11
/**
12
* Hook called before component starts rendering
13
* Supports both synchronous and asynchronous operations
14
* @param callback - Function to execute, can return Promise
15
*/
16
function onWillStart(callback: () => void | Promise<void>): void;
17
```
18
19
**Usage Examples:**
20
21
```typescript
22
import { Component, xml, onWillStart, useState } from "@odoo/owl";
23
24
class DataLoader extends Component {
25
static template = xml`
26
<div>
27
<div t-if="state.loading">Loading...</div>
28
<div t-else="">
29
<h2><t t-esc="state.data.title" /></h2>
30
<p><t t-esc="state.data.description" /></p>
31
</div>
32
</div>
33
`;
34
35
setup() {
36
this.state = useState({
37
loading: true,
38
data: null
39
});
40
41
// Async data loading before component renders
42
onWillStart(async () => {
43
try {
44
const response = await fetch("/api/data");
45
this.state.data = await response.json();
46
} catch (error) {
47
console.error("Failed to load data:", error);
48
this.state.data = { title: "Error", description: "Failed to load" };
49
} finally {
50
this.state.loading = false;
51
}
52
});
53
}
54
}
55
```
56
57
### onMounted
58
59
Called after the component has been mounted to the DOM.
60
61
```typescript { .api }
62
/**
63
* Hook called after component is mounted to DOM
64
* @param callback - Function to execute after mounting
65
*/
66
function onMounted(callback: () => void): void;
67
```
68
69
**Usage Examples:**
70
71
```typescript
72
import { Component, xml, onMounted, useRef } from "@odoo/owl";
73
74
class ChartComponent extends Component {
75
static template = xml`
76
<div>
77
<canvas t-ref="canvas" width="400" height="300"></canvas>
78
</div>
79
`;
80
81
setup() {
82
this.canvasRef = useRef("canvas");
83
84
onMounted(() => {
85
// DOM is ready, we can manipulate elements
86
const canvas = this.canvasRef.el;
87
const ctx = canvas.getContext("2d");
88
89
// Draw chart
90
this.drawChart(ctx);
91
92
// Focus management
93
if (this.props.autoFocus) {
94
canvas.focus();
95
}
96
97
// Third-party library initialization
98
this.chartInstance = new ThirdPartyChart(canvas, this.props.chartData);
99
});
100
}
101
102
drawChart(ctx) {
103
// Chart drawing logic
104
ctx.fillStyle = "blue";
105
ctx.fillRect(0, 0, 100, 50);
106
}
107
}
108
```
109
110
### onWillUpdateProps
111
112
Called before component props are updated.
113
114
```typescript { .api }
115
/**
116
* Hook called before props update
117
* @param callback - Function receiving the next props, can return Promise
118
*/
119
function onWillUpdateProps(callback: (nextProps: any) => void | Promise<void>): void;
120
```
121
122
**Usage Examples:**
123
124
```typescript
125
import { Component, xml, onWillUpdateProps, useState } from "@odoo/owl";
126
127
class UserProfile extends Component {
128
static template = xml`
129
<div>
130
<img t-att-src="state.avatar" alt="User avatar" />
131
<h2><t t-esc="props.user.name" /></h2>
132
<div t-if="state.loading">Loading profile...</div>
133
</div>
134
`;
135
136
setup() {
137
this.state = useState({
138
avatar: null,
139
loading: false
140
});
141
142
// Load avatar when user changes
143
onWillUpdateProps(async (nextProps) => {
144
if (nextProps.user.id !== this.props.user.id) {
145
this.state.loading = true;
146
try {
147
const response = await fetch(`/api/users/${nextProps.user.id}/avatar`);
148
this.state.avatar = await response.text();
149
} finally {
150
this.state.loading = false;
151
}
152
}
153
});
154
}
155
}
156
```
157
158
### onWillRender
159
160
Called before each rendering cycle.
161
162
```typescript { .api }
163
/**
164
* Hook called before each render
165
* @param callback - Function to execute before rendering
166
*/
167
function onWillRender(callback: () => void): void;
168
```
169
170
**Usage Examples:**
171
172
```typescript
173
import { Component, xml, onWillRender, useState } from "@odoo/owl";
174
175
class AnimatedComponent extends Component {
176
static template = xml`
177
<div t-att-class="state.cssClass">
178
<p>Render count: <t t-esc="state.renderCount" /></p>
179
</div>
180
`;
181
182
setup() {
183
this.state = useState({
184
renderCount: 0,
185
cssClass: ""
186
});
187
188
onWillRender(() => {
189
// Increment render counter
190
this.state.renderCount++;
191
192
// Prepare animations or computations
193
this.state.cssClass = this.state.renderCount % 2 === 0 ? "even-render" : "odd-render";
194
195
console.log("About to render, count:", this.state.renderCount);
196
});
197
}
198
}
199
```
200
201
### onRendered
202
203
Called after each rendering cycle completes.
204
205
```typescript { .api }
206
/**
207
* Hook called after each render completes
208
* @param callback - Function to execute after rendering
209
*/
210
function onRendered(callback: () => void): void;
211
```
212
213
**Usage Examples:**
214
215
```typescript
216
import { Component, xml, onRendered, useRef, useState } from "@odoo/owl";
217
218
class ScrollableList extends Component {
219
static template = xml`
220
<div t-ref="container" class="scrollable-list">
221
<div t-foreach="state.items" t-as="item" t-key="item.id">
222
<t t-esc="item.text" />
223
</div>
224
</div>
225
`;
226
227
setup() {
228
this.containerRef = useRef("container");
229
this.state = useState({
230
items: [],
231
shouldScrollToBottom: false
232
});
233
234
onRendered(() => {
235
// Scroll to bottom after new items are added
236
if (this.state.shouldScrollToBottom && this.containerRef.el) {
237
this.containerRef.el.scrollTop = this.containerRef.el.scrollHeight;
238
this.state.shouldScrollToBottom = false;
239
}
240
241
// Update third-party library after DOM changes
242
if (this.chartInstance) {
243
this.chartInstance.update();
244
}
245
});
246
}
247
248
addItem(text) {
249
this.state.items.push({ id: Date.now(), text });
250
this.state.shouldScrollToBottom = true;
251
}
252
}
253
```
254
255
### onWillPatch
256
257
Called before DOM patching occurs.
258
259
```typescript { .api }
260
/**
261
* Hook called before DOM patch
262
* @param callback - Function to execute before patching
263
*/
264
function onWillPatch(callback: () => void): void;
265
```
266
267
**Usage Examples:**
268
269
```typescript
270
import { Component, xml, onWillPatch, useRef } from "@odoo/owl";
271
272
class VideoPlayer extends Component {
273
static template = xml`
274
<video t-ref="video" t-att-src="props.src" controls="true" />
275
`;
276
277
setup() {
278
this.videoRef = useRef("video");
279
280
onWillPatch(() => {
281
// Save current playback state before DOM changes
282
const video = this.videoRef.el;
283
if (video) {
284
this.savedTime = video.currentTime;
285
this.wasPaused = video.paused;
286
}
287
});
288
}
289
}
290
```
291
292
### onPatched
293
294
Called after DOM patching completes.
295
296
```typescript { .api }
297
/**
298
* Hook called after DOM patch completes
299
* @param callback - Function to execute after patching
300
*/
301
function onPatched(callback: () => void): void;
302
```
303
304
**Usage Examples:**
305
306
```typescript
307
import { Component, xml, onPatched, onWillPatch, useRef } from "@odoo/owl";
308
309
class VideoPlayer extends Component {
310
static template = xml`
311
<video t-ref="video" t-att-src="props.src" controls="true" />
312
`;
313
314
setup() {
315
this.videoRef = useRef("video");
316
317
onWillPatch(() => {
318
// Save state before patch
319
const video = this.videoRef.el;
320
if (video) {
321
this.savedTime = video.currentTime;
322
this.wasPaused = video.paused;
323
}
324
});
325
326
onPatched(() => {
327
// Restore state after patch
328
const video = this.videoRef.el;
329
if (video && this.savedTime !== undefined) {
330
video.currentTime = this.savedTime;
331
if (!this.wasPaused) {
332
video.play();
333
}
334
}
335
});
336
}
337
}
338
```
339
340
### onWillUnmount
341
342
Called before the component is unmounted from the DOM.
343
344
```typescript { .api }
345
/**
346
* Hook called before component unmounts from DOM
347
* @param callback - Function to execute before unmounting
348
*/
349
function onWillUnmount(callback: () => void): void;
350
```
351
352
**Usage Examples:**
353
354
```typescript
355
import { Component, xml, onWillUnmount, onMounted } from "@odoo/owl";
356
357
class TimerComponent extends Component {
358
static template = xml`
359
<div>Timer: <t t-esc="state.seconds" />s</div>
360
`;
361
362
setup() {
363
this.state = useState({ seconds: 0 });
364
365
onMounted(() => {
366
// Start timer
367
this.interval = setInterval(() => {
368
this.state.seconds++;
369
}, 1000);
370
});
371
372
onWillUnmount(() => {
373
// Cleanup timer
374
if (this.interval) {
375
clearInterval(this.interval);
376
}
377
378
// Save state to localStorage
379
localStorage.setItem("timerState", JSON.stringify({
380
seconds: this.state.seconds,
381
timestamp: Date.now()
382
}));
383
});
384
}
385
}
386
```
387
388
### onWillDestroy
389
390
Called before the component is completely destroyed.
391
392
```typescript { .api }
393
/**
394
* Hook called before component is destroyed
395
* @param callback - Function to execute before destruction
396
*/
397
function onWillDestroy(callback: () => void): void;
398
```
399
400
**Usage Examples:**
401
402
```typescript
403
import { Component, xml, onWillDestroy, onMounted } from "@odoo/owl";
404
405
class WebSocketComponent extends Component {
406
static template = xml`
407
<div>
408
<p>Status: <t t-esc="state.status" /></p>
409
<p>Messages: <t t-esc="state.messages.length" /></p>
410
</div>
411
`;
412
413
setup() {
414
this.state = useState({
415
status: "disconnected",
416
messages: []
417
});
418
419
onMounted(() => {
420
// Initialize WebSocket connection
421
this.ws = new WebSocket("ws://localhost:8080");
422
423
this.ws.onopen = () => {
424
this.state.status = "connected";
425
};
426
427
this.ws.onmessage = (event) => {
428
this.state.messages.push(JSON.parse(event.data));
429
};
430
431
this.ws.onclose = () => {
432
this.state.status = "disconnected";
433
};
434
});
435
436
onWillDestroy(() => {
437
// Close WebSocket connection
438
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
439
this.ws.close();
440
}
441
442
// Unsubscribe from global services
443
GlobalEventBus.off("data-update", this.handleDataUpdate);
444
445
// Clean up any pending async operations
446
if (this.pendingRequest) {
447
this.pendingRequest.abort();
448
}
449
});
450
}
451
}
452
```
453
454
### onError
455
456
Called when an error occurs in the component or its children.
457
458
```typescript { .api }
459
/**
460
* Hook called when component or child errors occur
461
* @param callback - Function to handle errors
462
*/
463
function onError(callback: (error: Error) => void): void;
464
```
465
466
**Usage Examples:**
467
468
```typescript
469
import { Component, xml, onError, useState } from "@odoo/owl";
470
471
class ErrorBoundary extends Component {
472
static template = xml`
473
<div>
474
<div t-if="state.hasError" class="error-message">
475
<h3>Something went wrong!</h3>
476
<p><t t-esc="state.errorMessage" /></p>
477
<button t-on-click="resetError">Try Again</button>
478
</div>
479
<div t-else="">
480
<t t-slot="default" />
481
</div>
482
</div>
483
`;
484
485
setup() {
486
this.state = useState({
487
hasError: false,
488
errorMessage: ""
489
});
490
491
onError((error) => {
492
console.error("Component error caught:", error);
493
494
this.state.hasError = true;
495
this.state.errorMessage = error.message;
496
497
// Report error to monitoring service
498
this.reportError(error);
499
});
500
}
501
502
resetError() {
503
this.state.hasError = false;
504
this.state.errorMessage = "";
505
}
506
507
reportError(error) {
508
// Send to error tracking service
509
fetch("/api/errors", {
510
method: "POST",
511
headers: { "Content-Type": "application/json" },
512
body: JSON.stringify({
513
error: error.message,
514
stack: error.stack,
515
timestamp: new Date().toISOString()
516
})
517
});
518
}
519
}
520
```