Tracking

observe, when, computed, and React components automatically track when you call get() or use array looping functions, so you generally don't have to think about it unless you want to specifically optimize. When an observable is tracked, it will re-run on any changes anywhere within the observable and it's children.

These operations track:

  1. Call get() on an observable: settings.get()
  2. Array looping functions (shallow listener): arr.map(settings.accounts, () => ...)
  3. Accessing array length (shallow listener): if (arr.length > 0) ...
  4. Object.keys (shallow listener): Object.keys(settings)

These operation do not track:

  1. Accessing through an observable: state.settings
  2. Call get(false) on an observable: settings.get(false)
  3. Call peek() on an observable: settings.peek()

Some examples of the automatic behavior:

const state = observable({
    settings: {
        theme: 'dark'
    },
    chats: {
        messages: [
            { id: 0, text: 'hi' }
        ]
    }
})

observe(() => {
    // Example 1:
    const theme = state.settings.theme.get()
    // Tracking [state.settings.theme] because of get()

    // Example 2:
    const settings = state.settings
    // Not tracking because it's an object

    const theme = settings.theme.get()
    // Tracking [state.settings.theme] because of get()

    // Example 3:
    const themeObs = state.settings.theme
    // Not tracking with no get()

    // Example 4:
    state.chats.messages.map(m => <Message key={m.get().id} message={m} />)
    // Tracking [state.chats.messages (shallow)] because of map()

    // Example 5:
    Object.keys(state.settings)
    // Tracking [state.settings (shallow)]
})

The automatic behavior can be modified with two observable functions:

FunctionValueTracked
get()rawyes
get(false)rawno
peek()rawno

get()

get returns the raw data of an observable and tracks it, so you can work with it without doing any further tracking. You may want to use get() to:

  • Get the value of an observable wrapper of a primitive
  • Track this object and not its individual fields. Minimizing the number of listeners is better for performance.

get() is a tracking operation by default, but you can disable tracking with get(false).

    const theme = state.settings.theme.get()
    // ✅ Tracking [state.settings.theme]

    const theme = state.settings.theme.get(false)
    // ❌ Not tracked

peek()

peek returns the raw data of an observable without tracking it. It's the same as get(false).

shallow modifier

get() observes recursively by default, so any child changing will cause an update. You can modify it to be a shallow listener by just adding a true parameter. This can be useful when a component only needs to re-render if an object's keys or an array's items change.

const state = observable({ messages: [] })

observe(() => {
    // Only need this to update when messages added/removed
    const messages = state.messages.get(true)

    console.log('Latest message', messages[0])
})