You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Recursive component calls without `OnPush` or memoization cause exponential growth in change detection checks, blocking the main thread during deep tree rendering.
123
131
### β Best Practice
124
-
Using the `Memoization` pattern or `computed()` to prepare the tree data structure.
125
-
126
-
132
+
```typescript
133
+
@Component({
134
+
selector: 'app-tree-node',
135
+
standalone: true,
136
+
imports: [CommonModule],
137
+
changeDetection: ChangeDetectionStrategy.OnPush,
138
+
template: `
139
+
{{ node().name }}
140
+
@for (child of node().children; track child.id) {
141
+
<app-tree-node [node]="child" />
142
+
}
143
+
`
144
+
})
145
+
exportclassTreeNodeComponent {
146
+
node =input.required<TreeNode>();
147
+
}
148
+
```
127
149
### π Solution
128
-
This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries.
150
+
Use standalone components with `ChangeDetectionStrategy.OnPush` and modern `@for` control flow for recursive structures. This ensures change detection only runs when inputs change, drastically improving performance for deeply nested trees.
129
151
## β‘ 38. Global Styles Leakage
130
152
> [!NOTE]
131
153
> **Context:** CSS Encapsulation
@@ -181,54 +203,106 @@ Pay attention to template determinism with SSR.
181
203
> [!NOTE]
182
204
> **Context:** DI Performance
183
205
### β Bad Practice
184
-
Calling `inject()` inside a function that loops.
206
+
```typescript
207
+
processItems(items: Item[]) {
208
+
items.forEach(item=> {
209
+
const logger =inject(LoggerService);
210
+
logger.log(item.name);
211
+
});
212
+
}
213
+
```
185
214
### β οΈ Problem
186
-
Although `inject` is fast, in hot paths these are unnecessary DI tree lookups.
215
+
Although `inject()` is fast, calling it inside hot paths (loops) triggers unnecessary Dependency Injection tree lookups on every iteration, which degrades performance.
187
216
### β Best Practice
188
-
Inject dependency once at the class/file constant level.
189
-
217
+
```typescript
218
+
exportclassItemProcessor {
219
+
private logger =inject(LoggerService);
190
220
221
+
processItems(items:Item[]) {
222
+
items.forEach(item=> {
223
+
this.logger.log(item.name);
224
+
});
225
+
}
226
+
}
227
+
```
191
228
### π Solution
192
-
This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries.
229
+
Inject dependencies exactly once at the class or property level. This caches the reference to the service, bypassing redundant DI resolution and keeping hot paths efficient.
193
230
## β‘ 43. Unused Signal Dependencies
194
231
> [!NOTE]
195
232
> **Context:** Signal Graph
196
233
### β Bad Practice
197
-
Reading a signal inside `computed` whose value doesn't affect the result (an unexecuted logical branch).
234
+
```typescript
235
+
effect(() => {
236
+
console.log('Value changed:', this.value());
237
+
this.analytics.track('Change', this.user()?.id);
238
+
});
239
+
```
198
240
### β οΈ Problem
199
-
Angular dynamically builds the dependency graph. If you accidentally read a signal, it becomes a dependency.
241
+
Angular dynamically builds the signal graph. If you read a signal like `this.user()` inside an effect just for analytics, any change to `user()` will unexpectedly re-trigger the effect, leading to redundant executions.
200
242
### β Best Practice
201
-
Use `untracked()` to read signals whose changes should not trigger a recalculation.
202
-
203
-
243
+
```typescript
244
+
effect(() => {
245
+
const currentVal =this.value();
246
+
untracked(() => {
247
+
this.analytics.track('Change', this.user()?.id);
248
+
});
249
+
console.log('Value changed:', currentVal);
250
+
});
251
+
```
204
252
### π Solution
205
-
This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries.
253
+
Use `untracked()` to read signals that should not register as dependencies. This prevents unintended re-evaluations and ensures effects only run when their primary state changes.
Increases DOM tree depth, slowing down Style Recalculationand Layout.
266
+
Unnecessary wrapper `<div>` elements deeply nest the DOM tree ("div soup"). This exponentially slows down CSS Style Recalculation, Layout (Reflow), and Paint.
215
267
### β Best Practice
216
-
Use `<ng-container>` to group elements without creating extra DOM nodes.
This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries.
276
+
Utilize `<ng-container>` to apply structural logic or apply classes directly to component hosts. `<ng-container>` is rendered as an invisible comment, keeping the DOM tree shallow and performant.
221
277
## β‘ 45. Neglecting `runOutsideAngular` for Events
222
278
> [!NOTE]
223
279
> **Context:** High-frequency events
224
280
### β Bad Practice
225
-
`@HostListener('window:scroll')`
281
+
```typescript
282
+
@HostListener('window:scroll', ['$event'])
283
+
onScroll() {
284
+
this.scrollPosition.set(window.scrollY);
285
+
}
286
+
```
226
287
### β οΈ Problem
227
-
Every scrollevent triggers Change Detection.
288
+
Every scroll, mousemove, or drag event triggers a full Angular Change Detection cycle. High-frequency events will cause immediate UI lag and frame drops.
228
289
### β Best Practice
229
-
Subscribe manually in `runOutsideAngular` and update the signal only when necessary.
230
-
290
+
```typescript
291
+
exportclassScrollTracker {
292
+
private zone =inject(NgZone);
293
+
scrollPosition =signal(0);
231
294
295
+
constructor() {
296
+
this.zone.runOutsideAngular(() => {
297
+
window.addEventListener('scroll', () => {
298
+
if (Math.abs(window.scrollY-this.scrollPosition()) >50) {
This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries.
307
+
Bind high-frequency events outside the Angular Zone using `NgZone.runOutsideAngular()`. Only re-enter the Angular Zone (`zone.run()`) when a threshold is met and a UI update is strictly required.
If you define synchronous functions and bind them to events, Qwik must bundle all that javascript code eagerly, undermining resumability and slowing down the initial page load time.
Ensure all event handlers use the `$` suffix (like `onClick$`) and their corresponding logic is wrapped in `$()`. This explicit syntax breaks the application into tiny resumable closures that Qwik can fetch only when the user interacts with them.
Objects like WebSockets, DOM elements, Timeouts, or native Maps/Sets cannot be JSON serialized. Putting them into `useStore` breaks Qwik's core serialization engine, causing fatal errors when the server attempts to transmit state to the client for resumability.
// Use useSignal initialized with undefined on server
48
+
const ws =useSignal<WebSocket>();
49
+
50
+
// Only run in browser (where websocket lives)
51
+
useVisibleTask$(() => {
52
+
ws.value=newWebSocket('ws://localhost:8080');
53
+
return () =>ws.value?.close();
54
+
});
55
+
56
+
return <div>Connecting...</div>;
57
+
});
58
+
```
59
+
### π Solution
60
+
Do not put instances like WebSockets or DOM references into `useStore`. Use `useSignal()` when you need isolated references that initialize lazily on the client using `useVisibleTask$()`, or handle them outside the reactive serialization boundaries.
Using standard `.map()` for array rendering creates new DOM nodes for every element when the array changes, even if only one item is added or modified. This causes high CPU overhead and negates SolidJS's fine-grained reactivity.
38
+
### β Best Practice
39
+
```tsx
40
+
import { For } from'solid-js';
41
+
42
+
function List(props) {
43
+
return (
44
+
<ul>
45
+
<Foreach={props.items}>
46
+
{(item) => <li>{item.name}</li>}
47
+
</For>
48
+
</ul>
49
+
);
50
+
}
51
+
```
52
+
### π Solution
53
+
Always utilize the built-in `<For>` component. It caches DOM elements and handles granular updates when the array changes, reusing nodes instead of discarding and recreating them.
0 commit comments