JBrowse 2 · React App examples

Observe state with onChange

onChange fires on every MST patch — persist the session, drive undo/redo, or sync external UI.

Pass an onChange callback to <JBrowse> (alongside the assemblies/tracks/ views from the basic example) to be notified on every MST patch. It receives the forward patch and its inverse, so you can persist the session, build an undo/redo stack, or sync external UI:

<JBrowse
  assemblies={assemblies}
  tracks={tracks}
  views={views}
  onChange={(patch, reversePatch) => {
    // patch: { op, path, value } — the change that was just applied
    // reversePatch: the patch that would undo it
  }}
/>

A common use is autosaving the session to localStorage so a reload restores where the user left off. That needs a full snapshot rather than just the patch, and restoring a saved session on mount needs a config.defaultSession you control directly — both call for the unmanaged createViewState + <JBrowseApp> flow instead of the managed component:

// @jbrowse/mobx-state-tree is JBrowse's bundled MST fork
import { getSnapshot } from '@jbrowse/mobx-state-tree'

const saved = localStorage.getItem('jbrowse-session')
const state = createViewState({
  config: {
    ...config,
    defaultSession: saved ? JSON.parse(saved) : config.defaultSession,
  },
  // runs after construction, so referencing `state` here is safe
  onChange: () => {
    localStorage.setItem(
      'jbrowse-session',
      JSON.stringify(getSnapshot(state.session)),
    )
  },
})

The patch path strings mirror the session state tree. The BaseSessionModel and per-view model docs (e.g. LinearGenomeView) show which properties live where, so you can tell which patches matter for your use case.

Live demo

View source
import { useState } from 'react'

import { JBrowse } from '@jbrowse/react-app2'

import { volvoxConfig } from '../volvoxConfig.ts'

// onChange fires on every MST patch. Use it to persist the session (e.g. to
// localStorage or a backend), drive an undo/redo stack, or sync external UI.
export default function WithOnChange() {
  const [log, setLog] = useState<string[]>([])

  return (
    <div>
      <div
        style={{
          padding: 8,
          fontFamily: 'monospace',
          fontSize: 12,
          background: '#8881',
        }}
      >
        <div>Recent session patches (pan/zoom, or show/hide a track):</div>
        <pre style={{ margin: 0 }}>
          {log.join('\n') || '(interact with the view to see patches)'}
        </pre>
      </div>
      <JBrowse
        assemblies={volvoxConfig.assemblies}
        tracks={volvoxConfig.tracks}
        views={[
          {
            type: 'LinearGenomeView',
            init: {
              assembly: 'volvox',
              loc: 'ctgA:1..50000',
              tracks: ['volvox_cram'],
            },
          },
        ]}
        onChange={patch => {
          setLog(prev => [`${patch.op} ${patch.path}`, ...prev].slice(0, 8))
        }}
      />
    </div>
  )
}