UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【Mobx】Reactとの統合

MobXとReactの統合について

mobx.js.org

mobx-react-liteライブラリから提供されるobserverを使うことで、必要な変更があった場合にのみ再レンダリングされる

基本的な使い方は以下

import React from "react"
import ReactDOM from "react-dom"
import { makeAutoObservable } from "mobx"
import { observer } from "mobx-react-lite"

class Timer {
    secondsPassed = 0

    constructor() {
        makeAutoObservable(this)
    }

    increaseTimer() {
        this.secondsPassed += 1
    }
}

const myTimer = new Timer()


const TimerView = observer(({ timer }) => <span>Seconds passed: {timer.secondsPassed}</span>)

ReactDOM.render(<TimerView timer={myTimer} />, document.body)

setInterval(() => {
    myTimer.increaseTimer()
}, 1000)

timer.secondsPassedが監視対象になっており、 変更されるとコンポーネントが自動的に再レン​​ダリングされる

配列もオブジェクトの要素の変更も監視しており、todos[0].author.displayNameのような深いパラメータに対しても検知する

主なポイント

外部の状態管理に利用できる
observerは外部状態を、propsとして渡すことも、グローバル変数として使うことも、ReactContextで使用することも可能

ReactContextで使用する例は以下

import {observer} from 'mobx-react-lite'
import {createContext, useContext} from "react"

const TimerContext = createContext<Timer>()

const TimerView = observer(() => {
    // Grab the timer from the context.
    const timer = useContext(TimerContext) // See the Timer definition above.
    return (
        <span>Seconds passed: {timer.secondsPassed}</span>
    )
})

ReactDOM.render(
    <TimerContext.Provider value={new Timer()}>
        <TimerView />
    </TimerContext.Provider>,
    document.body
)

ローカルな状態管理に利用できる
ローカルな状態としてuseStateで利用可能

import { observer } from "mobx-react-lite"
import { useState } from "react"

const TimerView = observer(() => {
    const [timer] = useState(() => new Timer())
    return <span>Seconds passed: {timer.secondsPassed}</span>
})

クラスを使用する代わりに、監視可能なオブジェクトを直接作成することもできる

import { observer } from "mobx-react-lite"
import { observable } from "mobx"
import { useState } from "react"

const TimerView = observer(() => {
    const [timer] = useState(() =>
        observable({
            secondsPassed: 0,
            increaseTimer() {
                this.secondsPassed++
            }
        })
    )
    return <span>Seconds passed: {timer.secondsPassed}</span>
})

ReactDOM.render(<TimerView />, document.body)

上を簡潔に書くためのuseLocalObservableというフックも用意されている

import { observer, useLocalObservable } from "mobx-react-lite"

const TimerView = observer(() => {
    const timer = useLocalObservable(() => ({
        secondsPassed: 0,
        increaseTimer() {
            this.secondsPassed++
        }
    }))
    return <span>Seconds passed: {timer.secondsPassed}</span>
})

ReactDOM.render(<TimerView />, document.body)

ただ、React Suspense などのメカニズムの一部の機能が使用できなくなる可能性があるためuseStateが使えるところは使った方が良い