Tracing

If you notice your components feeling too slow or seeming to render too often, two helpful functions can show you exactly what observables they're listening to and why they're rendering.

traceListeners()

Call traceListeners() anywhere within observe or any React component to console.log a list of every observable being track for changes. This can help you find and reduce the number of listeners.

import { traceListeners } from "legendapp/state/trace"

function Component(props) {

    // Call traceListeners anywhere inside the component
    traceListeners()

    return (
        <div>{state.count.get()}</div>
    )

    /* This logs:

    [legend-state] tracking 1 observable:
    1: count

    */
}

traceUpdates()

Call traceUpdates() anywhere within observe or any React component to console.log information about the observable change that causes each render. This can help you track down why components are rendering too often.

import { traceUpdates } from "legendapp/state/trace"

function Component(props) {
    // Call traceUpdates anywhere inside the component
    traceUpdates()

    return (
        <div>{state.count.get()}</div>
    )

    /* This logs:

    [legend-state] Rendering because "count" changed:
    from: 0
    to: 1

    */
}

verifyNotTracking()

Call verifyNotTracking() anywhere within any React component to console.error if it is tracking anything. This is useful if you are going for fine-grained reactivity and want to make sure parent components are not tracking any observables.

import { verifyNotTracking } from "legendapp/state/trace"

function Component(props) {
    // Call verifyNotTracking anywhere inside the component
    verifyNotTracking()

    // This will log an error because get() makes it track
    return (
        <div>{state.count.get()}</div>
    )
}
function FineComponent(props) {
    // Call verifyNotTracking anywhere inside the component
    verifyNotTracking()

    // This will not log because rendering the observable directly
    // does not re-render this component
    return (
        <div>{state.count}</div>
    )
}

What to do with this information

  • You may want to call get() at a higher level in an object to only listen to it, and not every single child.
  • Use a shallow listener with get(true) to only update when keys are added to or removed from the object.
  • You may want to call peek() to not listen at all.

If you find an observable changing often and you're not sure why, you can put a breakpoint on the console log to catch it. Or add your own listener to the observable to watch every change:

// Why is count rendering so often?
state.count.onChange(value => {
    console.log('Count changed', value);
    console.trace();
    debugger;
})