Mobx, React, Typescript: Optimizing to re-render a lot of data

Posted on: 2020-03-18
Software versions: MobX 4, MobX 5, Typescript 3.7

If you have to render a lot of data that changes often, using Mobx wisely could help you build a smooth and responsive ui. If you use mobx-react observer, you should try to tie the smallest component possible to the store and not the entire array.

If you have the following store, that represent a table of colors:

class DataStore { @observable dataMap: Map<string, string> = new Map(); width = 50; height = 50; constructor () { let i = -1; while (i++ < this.width) { let j = -1; while (j++ < this.height) { this.dataMap.set(`${i}-${j}`, 'black'); } } } updateRandomCell () { const x = Math.floor(Math.random() * this.width); const y = Math.floor(Math.random() * this.height); const key = `${x}-${y}`; this.dataMap.set(key, this.dataMap.get(key) === 'black' ? 'red' : 'black'); } }

To render this table efficiently, the best thing to do is, instead of doing one big component tht renders everything, to split the code so that every cell is its own component that connect directly to the store. That way, you don't have to do a full re-render everytime a cell data changes. Here is the example app and the two codes compared:

<div id="root-mobx-example-1"></div> <script src="/statics/mobx-example1.js"></script> <div> &nbsp; </div>
// Without Child Component (The Parent Component is Directly Connected to the Store): const Square: React.FC<any> = observer(() => { return ( <div> { Array.from({ length : store.width }).map((_, i) => { return ( <div style={{ display: 'flex' }}> { Array.from({ length : store.height }).map((_, j) => { return ( <div style={{ width: 3, height: 3, background: store.dataMap.get(`${i}-${j}`) }} > </div> ) }) } </div> ) }) } </div> ) })
// With Child Compponents Directly Connected to the Store: const Cell: React.FC<any> = observer((props: any) => { return ( <div style={{ width: 3, height: 3, background: store.dataMap.get(`${props.i}-${props.j}`) }} > </div> ); }); const SquareWithCells: React.FC<any> = observer(() => { return ( <div> { Array.from({ length : store.width }).map((_, i) => { return ( <div style={{ display: 'flex' }}> { Array.from({ length : store.height }).map((_, j) => { return ( <Cell i={i} j={j}/> ) }) } </div> ) }) } </div> ) })