Tracking

observe, when, computed, and React observer 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 its 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 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(true)rawshallow
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.
    const theme = state.settings.theme.get()
    // ✅ Tracking [state.settings.theme]

peek()

peek() returns the raw value in the same way as get(), but it does not automatically track it. Use this when you don't want the component/observing context to update when the value changes.

    const theme = state.settings.theme.peek()
    // Not tracking

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])
})