github mobxjs/mobx-state-tree v9.0.0

4 hours ago

Version 9.0.0 is mostly a performance release. We bumped the major version because it also includes a few behavior changes, described in the migration notes below. Big thanks to @lavrton and @cpruijsen, who are both first-time contributors!

MST 9 makes model creation about 2x faster and applySnapshot about 4x faster. It's much faster still in large trees: actions that change many nodes run about 10x faster when you aren't reading snapshots, detaching or moving nodes goes from hundreds of milliseconds to a few, and undoing to an earlier snapshot no longer re-applies every child. We've benchmarked across a few contributors, but please let us know if you have more benchmark data and can verify these results more clearly.

Breaking Changes

  • fix: throw when a model member is redeclared across actions, views or volatile state by @cpruijsen in #2304
  • perf: compute snapshots when they are read, not after every action by @lavrton in #2308 (also a big performance win)
  • fix: make walk visit every node in the tree by @cpruijsen in #2305 (we consider this a fix, but it may break you if you relied on the prior behavior)

Features

  • Added setMemberRedeclarationChecking and getMemberRedeclarationChecking to support #2304

Fixes

  • fix(array): reconcile by identifier when applySnapshot moves children by @lavrton in #2311
  • fix: keep snapshot identity when a node is instantiated lazily by @lavrton in #2310

Performance

  • perf: detach walks only the detached subtree of the identifier cache by @lavrton in #2306
  • perf: look up each model property once in getSnapshot by @lavrton in #2307
  • perf: emit class fields as plain assignments by @lavrton in #2309

Development/Community

New Contributors

Full Changelog: v8.0.0...v9.0.0

Migration

For most users, migrating from v8 -> v9 should require no work. If you upgrade and your TypeScript types and runtime tests pass, you should be all set. Here are the changes that could affect you.

New Error Behavior when Redeclaring Members

Introduced in #2304, MST now throws at create() when a model declares the same name as two different kinds of member, like an action and a view that are both named anAction. In the past, the later declaration silently replaced the earlier one. This builds on the property override check we added in v7 (#2207).

[mobx-state-tree] 'anAction' already exists as an action and cannot be declared as a view

A few things to know:

  • Redeclaring a member of the same kind still works. For example, two .actions() blocks that both define afterCreate are fine.
  • types.compose keeps its override behavior, so a composed type can still override members from the types before it.
  • The check runs in development mode only by default, because it slows down model creation. If you want it in production too, call setMemberRedeclarationChecking(true).

This was probably a bug in your application code before. You can migrate by picking distinct names for your actions, views, and volatile state.

Snapshots are Computed When They're Read

Introduced in #2308, MST no longer rebuilds the root snapshot after every action. Now we only build it when something reads it, through getSnapshot or an onSnapshot listener. For an app that changes a lot of nodes in a single action and doesn't read snapshots, this is a huge speedup: in @lavrton's benchmark, one drag step across 600 elements went from 28ms to 2.4ms.

If you use getSnapshot and onSnapshot in the usual ways, you should see the same results. Some edge cases have changed:

  • A root onSnapshot listener added in the middle of an action no longer gets the changes made earlier in that action. Non-root listeners already worked this way.
  • A non-root onSnapshot listener added in the middle of an action now gets the changes made later in that action.
  • If building a snapshot throws (for example, from a custom type's toSnapshot), it now throws when the snapshot is read, not at the end of every action.

walk Visits Every Node

Introduced in #2305, walk now visits every node in the tree, including model, array, and map children that haven't been accessed yet. In the past, it skipped those subtrees, and sometimes only visited the root. This fixes #1433.

To visit those children, walk has to create their instances, so their afterCreate and afterAttach hooks may now run during a walk. If your hooks have side effects, or you relied on walk only visiting nodes you had already accessed, check those call sites.

Don't miss a new mobx-state-tree release

NewReleases is sending notifications on new releases.