Search code examples
reactjstypescriptmobx

Mobx not updating to changes in array


I'm having trouble getting MobX to render when updating array. Here is a simplified version of the code:

import React, { useState } from 'react'
import { flow, makeObservable, observable } from 'mobx'
import { observer } from 'mobx-react'

import { ResourceList } from './ResourceList'
import { ResourceItem } from './ResourceItem'


export const View = observer(() => {
    const [{
        items,
    }] = useState<MyState>(new MyState())

    return <ResourceList items={items} />
})


export class MyState {
    constructor() {
        makeObservable(this)
        this._fetch()
    }

    @observable public items: ResourceItem[] = []

    private _fetch = flow(function* (this: MyState) {
        const items = yield fetchItems('myitems')
        this.items = items
    })
}

As you can see I'm trying to consume the array in <ResourceList items={items} /> and it does not work.

The weird thing is that if works if I destruct the array like so <ResourceList items={[...items]} />

is also works if I just console.log a property of the array:

console.log(items.length)
return <ResourceList items={items} />

So it really looks like Mobx is not understanding that the array ins being used in the markup.

What would be the proper way to fix this? Isn't it supposed to have something I configure inside the state class so make it happen?

Thanks


Solution

  • This is discussed in the Understanding reactivity part of the documentation. When you are writing items you are not dereferencing a property of the items array, so your View observer component doesn't track the items array. You could slice the array or spread it like you did in your example.

    <ResourceList items={items.slice()} />
    

    You also want to create the MyState instance once with the help of a function given to useState, so you don't create an instance of the class every render that you don't use, and that will overwhelm the network.

    const [{ items }] = useState<MyState>(() => new MyState());