Child components based on computed properties -- when property changes, component is reinitialized


#1

In my app, when rendering a list of components based on a computed property, whenever that property is recomputed, each of the list of components gets blown away and reinitialized, instead of having its state preserved.

Background: I’m working on a carousel (clicking “next” advances you to the next item, and only one item is shown at a time). Each carousel item needs its own state, and I want each item’s image to be fully loaded before the item is shown. Hence, I have a carousel-container component for the carousel and the next button, which in its template renders an array of carousel-item components instead of only reusing one carousel-item component for the currently-shown item.

carousel-container has a property special-items, which is a computed property that returns an array of items. The template then loops over the special-items and renders a carousel-item component for each, handing it its item. Problem is, when the special-items property on carousel-container changes, the child components are all blown away and reinitialized. Any state they had is lost!

Here’s an example showing the issue: https://ember-twiddle.com/613a352115800a9a099045e720a43e6e?openFiles=components.main-component.js%2C

I’m assuming this is expected behavior with computed properties. I’m not quite sure why it would blow away and reinit the child components though; shouldn’t it just update each one’s item property with the newly-computed item?

Regardless, anyone see a way to get around the issue I’m facing, to be able to have the child components’ state preserved when the parent component’s properties change?


#2

If you give the each helper a key then it can use that instead to check if the elements in the array have changed. As a bonus, it also speeds up rerender on large lists

{{#each specialItems key='id' as |item|}}
	{{item-component item=item}}
{{/each}}

#3

Excellent, that fixes it indeed! Thank you! DOM elements are preserved and I can keep the component’s state around as expected.


#4

This is super powerful and solves a problem that we’ve been digging into recently with a large rapidly-updating table. I had no idea that was in the framework, and so easy to implement. Thanks!