8000 docs(template): new reconciliation algorithm docs · rx-angular/rx-angular@84f7d16 · GitHub
[go: up one dir, main page]

Skip to content

Commit 84f7d16

Browse files
committed
docs(template): new reconciliation algorithm docs
1 parent e004157 commit 84f7d16

File tree

7 files changed

+130
-5
lines changed

7 files changed

+130
-5
lines changed

apps/docs/docs/template/rx-for-directive.mdx

Lines changed: 130 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ title: 'RxFor'
66

77
import Tabs from '@theme/Tabs';
88
import TabItem from '@theme/TabItem';
9+
import ReactPlayer from 'react-player';
910

1011
## Motivation
1112

@@ -46,13 +47,17 @@ Each instance of `RxFor` can be configured to render with different settings.
4647

4748
<TabItem value="signal" label="Usage with signals">
4849

50+
:::info
51+
You don't need to unwrap the signal, just pass its reference to `rxFor`, it'll do the rest for you.
52+
:::
53+
4954
```html title="src/list.component.html"
5055
<div class="movie-list">
5156
<movie *rxFor="let movie of movies;" [movie]="movie" />
5257
</div>
5358
```
5459

55-
```typescript title="src/list.component.html"
60+
```typescript title="src/list.component.ts"
5661
import { RxFor } from '@rx-angular/template/for';
5762
import { Component } from '@angular/core';
5863

@@ -76,7 +81,7 @@ export class ListComponent {
7681
</div>
7782
```
7883

79-
```typescript title="src/list.component.html"
84+
```typescript title="src/list.component.ts"
8085
import { RxFor } from '@rx-angular/template/for';
8186
import { Component } from '@angular/core';
8287

@@ -105,7 +110,7 @@ export class ListComponent {
105110
</div>
106111
```
107112

108-
```typescript title="src/list.component.html"
113+
```typescript title="src/list.component.ts"
109114
import { RxFor } from '@rx-angular/template/for';
110115
import { Component } from '@angular/core';
111116

@@ -167,7 +172,7 @@ You can pass any valid property from the given input type as a shortcut instead
167172
</div>
168173
```
169174

170-
```typescript title="src/list.component.html"
175+
```typescript title="src/list.component.ts"
171176
import { RxFor } from '@rx-angular/template/for';
172177
import { Component } from '@angular/core';
173178

@@ -191,7 +196,7 @@ export class ListComponent {
191196
</div>
192197
```
193198

194-
```typescript title="src/list.component.html"
199+
```typescript title="src/list.component.ts"
195200
import { RxFor } from '@rx-angular/template/for';
196201
import { Component } from '@angular/core';
197202

@@ -330,6 +335,126 @@ The following context variables are available for each template:
330335
| `odd$` | `Observable<boolean>` | odd as `Observable` |
331336
| `select` | `(keys: (keyof T)[], distinctByMap) => Observable<Partial<T>>` | returns a selection function which accepts an array of properties to pluck out of every list item. The function returns the selected properties of the current list item as distinct `Observable` key-value-pair. |
332337

338+
## Use the new reconciliation algorithm
339+
340+
You can opt in to use the new reconciliation algorithm, which was shipped by the
341+
angular team as part of the new `@for` control flow.
342+
343+
The original implementations can be found [here](https://github.com/angular/angular/blob/main/packages/core/src/render3/list_reconciliation.ts) & [here](https://github.com/angular/angular/blob/f8d22a9ba4e426f14f9c7fd608e1ad752cd44eb5/packages/core/src/render3/instructions/control_flow.ts#L281)
344+
345+
By default, `rxFor` uses the `IterableDiffer` to calculate the operations it needs to apply
346+
when an update to the bound iterable happened.
347+
348+
<Tabs>
349+
350+
<TabItem value="opt-in" label="new reconciliation">
351+
352+
```typescript
353+
import { provideExperimentalRxForReconciliation } from '@rx-angular/template/for';
354+
355+
const appConfig: AppConfig = {
356+
providers: [provideExperimentalRxForReconciliation()],
357+
};
358+
```
359+
360+
</TabItem>
361+
362+
<TabItem value="opt-out" label="legacy IterableDiffer">
363+
364+
In case you want to opt-out at some level of the injector tree, you can use the `provideLegacyRxForReconciliation` provider function.
365+
366+
```typescript
367+
import { provideLegacyRxForReconciliation } from '@rx-angular/template/for';
368+
369+
@Component({
370+
providers: [provideLegacyRxForReconciliation()],
371+
})
372+
export class MyComponent {}
373+
```
374+
375+
</TabItem>
376+
377+
</Tabs>
378+
379+
### Impact
380+
381+
In general, the new reconciliation algorithm diffs two lists with fewer operations to achieve the same goal as the legacy `IterableDiffer` approach. However, this only applies for move / swap operations.
382+
383+
It's also more memory efficient than the iterable differ.
384+
385+
For `rxFor` specifically, there are also **behavioral impacts**.
386+
Instead of actually moving around DOM, the new reconciliation works by `detaching` & `attaching` views.
387+
As rxFor by default uses the concurrent mode, it splits each individual task (attach, detach, update, remove) and works them off in a queue.
388+
As we are operating on the DOM, we have to run tasks in the given order.
389+
The biggest impact is that you'll visually see views disappearing from the screen when the whole data set is being shuffled around.
390+
391+
This leads to visual instability on the one hand, but also makes sure no view is ever in the wrong position as in the legacy approach.
392+
393+
#### Swap
394+
395+
Swapping the first item with the last item. This shows off the advantages of the new reconciliation in the most impressive way.
396+
397+
<Tabs>
398+
<TabItem value="experimental-swap" label="Sw F438 ap new reconciliation">
399+
400+
The new reconciliation algorithm only needs 4 operations (detach x2, attach x2) to achieve the end result.
401+
402+
<ReactPlayer playing controls url={require('@site/static/img/template/rx-for/swap-experimental.mp4').default} />
403+
404+
</TabItem>
405+
<TabItem value="legacy-swap" label="Swap IterableDiffer">
406+
407+
The `IterableDiffer` approach needs to move around each item in the whole list to achieve the final result.
408+
409+
<ReactPlayer playing controls url={require('@site/static/img/template/rx-for/swap-legacy.mp4').default} />
410+
411+
</TabItem>
412+
</Tabs>
413+
414+
#### Random Shuffle
415+
416+
Randomly shuffle elements in the array. This example shows the behavioral changes.
417+
418+
<Tabs>
419+
<TabItem value="experimental-shuffle" label="Shuffle new reconciliation">
420+
421+
As stated before, the new reconciliation algorithm doesn't move dom, it detaches & attaches nodes.
422+
As rxFor schedules & runs all operations in order, it's possible that you will end up with a temporary state where nodes are detached
423+
but not attached yet.
424+
425+
<ReactPlayer playing controls url={require('@site/static/img/template/rx-for/shuffle-experimental.mp4').default} />
426+
427+
</TabItem>
428+
<TabItem value="legacy-shuffle" label="Shuffle IterableDiffer">
429+
430+
The legacy approach just moves around views until the final result is stable. This has the downside that a couple of views
431+
might be temporarily out of order.
432+
433+
<ReactPlayer playing controls url={require('@site/static/img/template/rx-for/shuffle-legacy.mp4').default} />
434+
435+
</TabItem>
436+
</Tabs>
437+
438+
#### Filter
439+
440+
Filter items and remove the filter again. Both approaches work the same in this scenario.
441+
442+
<Tabs>
443+
<TabItem value="experimental-swap" label="Swap new reconciliation">
444+
445+
<ReactPlayer playing controls url={require('@site/static/img/template/rx-for/filter-experimental.mp4').default} />
446+
447+
</TabItem>
448+
<TabItem value="legacy-swap" label="Swap IterableDiffer">
449+
450+
The legacy approach just moves around views until the final result is stable. This has the downside that a couple of views
451+
might be temporarily out of order.
452+
453+
<ReactPlayer playing controls url={require('@site/static/img/template/rx-for/filter-legacy.mp4').default} />
454+
455+
</TabItem>
456+
</Tabs>
457+
333458
## Advanced Usage
334459

335460
### Use render strategies (`strategy`)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)
0