Modifying an observable array does not update the view via the each directive
See original GitHub issueUsing push
to add items to an observable array does not update the view using the each
directive. Here is an example where clicking the “Add item” button does not add any new li
to the rendered ul
.
import {r} from 'mobx-jsx'
import {observable} from 'mobx'
function App() {
const items = observable([
{name: 'Item 0'},
])
const onAddItemClick = () => {
items.push({name: 'Item ' + items.length})
}
return <MyList items={items} onAddClick={onAddItemClick} />
}
function MyList({items, onAddClick}) {
return <>
<ul>
<$ each={items}>
{ item => <li>{(item.name + ` (items: ${items.length})`)}</li> }
</$>
</ul>
<button onClick={onAddClick}>Add item</button>
</>
}
Issue Analytics
- State:
- Created 4 years ago
- Comments:6 (3 by maintainers)
Top Results From Across the Web
knockout.js - Change in observable array in ... - Stack Overflow
The problem here seems to be that code: me.course()[0].subjectList().push(me.subjectObject()[0]);. Calling .subjectList().push(.
Read more >Docs • Svelte
Because Svelte's reactivity is based on assignments, using array methods like .push() and .splice() won't automatically trigger updates.
Read more >3 Common Rxjs Pitfalls (and how to avoid them)
These are 3 situations that we can come across while building Angular apps using RxJs. We are going to go over why the...
Read more >AsyncPipe - Angular
The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted. When a new value is emitted,...
Read more >Reactivity in Depth - Vue.js
When you modify them, the view updates. ... JavaScript doesn't usually work like this. ... When we mutate A0 , A2 does not...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Alright, so big changes to the runtime allows each framework to adopt their own syntax. I now have arrays working the way you expect (as well as support for class components). Instead of the old control flow, you use the
map
function that takes an observableArray as it’s first argument and map iterator as the 2nd. It properly handles of MobX array mutators. I use the technique you highlighted in your earlier post so thanks for that. I still use my optimized memoized mapper as well so it’s win-win.Released in v0.6.0
Thanks for looking into this in such detail.
I definitely don’t like the idea of creating N subscriptions, but I think it should be possible to get away with just a single subscription on the array level with a library like MobX. I’m pretty sure this is already what happens when you use methods like
slice
,map
,indexOf
, etc on an observable array in MobX. This is the code that handles these cases in MobX: https://github.com/mobxjs/mobx/blob/333dfe1719712f8e0729bca8b7f05960b2838f5c/src/types/observablearray.ts#L473Adjusting my example code to access one of these methods (in this example
slice
) does indeed solve the issue and the view is automatically updated whenpush
is used on the array:Observing arrays like this works out of the box in the context of React as there are no directives and the array methods can directly be used in the render method of a component. In React you would for example use
{items.map(item => <li>{(item.name)}</li>)}
instead of the directive, which will automatically mark the array as observed and rerender the component on array changes.Wouldn’t it be possible for this library to make use of the observe feature of MobX when the
each
directive is used? This should also make it possible to clean up in case the directive gets “unmounted” as observe returns a dispose function. On a side note, wouldn’t it theoretically be possible to use the change data provided to the observe callback to skip most of the work done inreconcileArrays
? I have no idea how much of a performance difference this would make, but it could be something interesting to explore.So far I only have a rough understanding of how the different libraries (mobx-jsx, dom-expressions, babel-plugin-jsx-dom-expressions) work together, but I assume that such logic would ideally be implemented in mobx-jsx as it looks to be specific to how MobX works.
I agree that immutability seems to be the direction more and more libraries and frameworks are taking, but it is very opinionated and unfortunately makes it impossible to integrate with third-party libraries that need to mutate arrays or objects in place. I also like the idea of simply being able to use standard array methods and everything just working out of the box with no special rules. Obviously there are many trade-offs involved, but if a solution without a negative impact on performance is possible, it might be worth it.