MobX v4.0.0 Release Notes

    • ๐Ÿš€ For the highlights of this release, read the blog:
    • ๐Ÿ‘€ For migration notes: see the wiki page
    • ๐Ÿšš Note; many things that were removed to make the api surface smaller. If you think some feature shouldn't have been removed, feel free to open an issue!

    This is the extensive list of all changes.

    ๐Ÿ†• New features

    ๐Ÿš€ The changes mentioned here are discussed in detail in the release highlights, or were simply updated in the docs.

    • ๐Ÿ— MobX 4 introduces separation between the production and non production build. The production build strips most typechecks, resulting in a faster and smaller build. Make sure to substitute process.env.NODE_ENV = "production" in your build process! If you are using MobX in a react project, you most probably already have set this up. Otherwise, the idea is explained here.
    • ๐Ÿ“ฆ Introduced flow to create a chain of async actions. This is the same function as asyncActions of the mobx-utils package
    • ๐Ÿ‘ป These flow's are now cancellable, by calling .cancel() on the returned promise, which will throw a cancellation exception into the generator function.
    • ๐Ÿ‘ flow also has experimental support for async iterators (async * function)
    • Introduced decorate(thing, decorators) to decorate classes or object without needing decorator syntax.
    • Introduced onBecomeObserved and onBecomeUnobserved. These API's enable hooking into the observability system and get notified about when an observable starts / stops becoming used. This is great to automaticaly fetch data from external resources, or stop doing so.
    • ๐Ÿ‘ป computed / @computed now accepts a requiresReaction option. If it set, the computed value will throw an exception if it is being read while not being tracked by some reaction.
    • ๐Ÿ”ง To make requiresReaction the default, use mobx.configure({ computedRequiresReaction: true })
    • ๐Ÿ”ง Introduced mobx.configure({ disableErrorBoundaries }), for easier debugging of exceptoins. By NaridaL through #1262
    • 0๏ธโƒฃ toJS now accepts the options: { detectCycles?: boolean, exportMapsAsObjects?: boolean }, both true by default
    • Observable maps are now backed by real ES6 Maps. This means that any value can be used as key now, not just strings and numbers.
    • โšก๏ธ The flow typings have been updated. Since this is a manual effort, there can be mistakes, so feel free to PR!

    • computed(fn, options?) / @computed(options) get fn() now accept the following options:

      • set: (value) => void to set a custom setter on the computed property
      • name: "debug name"
      • equals: fn the equality value to use for the computed to determine whether its output has changed. The default is comparer.default. Alternatives are comparer.structural, comparer.identity or just your own comparison function.
      • requiresReaction: boolean see above.
    • autorun(fn, options?) now accepts the following options:

      • delay: number debounce the autorun with the given amount of milliseconds. This replaces the MobX 3 api autorunAsync
      • name: "debug name"
      • scheduler: function a custom scheduler to run the autorun. For example to connect running the autorun to requestAnimationFrame. See the docs for more details
      • onError. A custom error handler to be notified when an autorun throws an exception.
    • reaction(expr, effect, options?) now accepts the following options:

      • delay: number debounce the autorun with the given amount of milliseconds. This replaces the MobX 3 api autorunAsync
      • fireImmediately. Immediately fire the effect function after the first evaluation of expr
      • equals. Custom equality function to determine whether the expr function differed from its previous result, and hence should fire effect. Accepts the same options as the equals option of computed.
      • All the options autorun accepts
    • when(predicate, effect?, options?) now accepts the following options:

      • name: "debug name"
      • onError. A custom error handler to be notified when an autorun throws an exception.
      • timeout: number a timeout in milliseconds, after which the onError handler will be triggered to signal the condition not being met within a certain time
    • The effect parameter of when has become optional. If it is omitted, when will return a promise. This makes it easy to await a condition, for example: await when(() => user.profile.loaded). The returned promise can be cancelled using promise.cancel()

    • There is now an utility API that enables manipulating observable maps, objects and arrays with the same api. These api's are fully reactive, which means that even new property declarations can be detected by mobx if set is used to add them, and values or keys to iterate them.

      • values(thing) returns all values in the collection as array
      • keys(thing) returns all keys in the collection as array
      • set(thing, key, value) or set(thing, { key: value }) Updates the given collection with the provided key / value pair(s).
      • remove(thing, key) removes the specified child from the collection. For arrays splicing is used.
      • has(thing, key) returns true if the collection has the specified observable property.
      • get(thing, key) returns the chlid under the specified key.
    • observable, observable.array, observable.object, observable.map and extendObservable now accept an additional options object, which can specify the following attributes:

      • name: "debug name"
      • deep: boolean. true by default, indicates whether the children of this collection are automatically converted into observables as well.
      • defaultDecorator: <decorator> specifies the default decorator used for new children / properties, by default: observable.deep, but could be changed to observable.ref, observable.struct etc. (The deep property is just a short-hand for switching between observable.deep or observable.ref as default decorator for new properties)

    ๐Ÿ’ฅ Breaking changes

    The changes mentioned here are discussed in detail in the migration notes

    • ๐Ÿ’ป MobX 4 requires Map to be globally available. Polyfill it if targeting IE < 11 or other older browsers.
    • ๐Ÿ”ง For typescript users, MobX now requires Map and several Symbols to exist for its typings. So make sure that the lib configuration of your project is set to "es6". (The compilation target can still be "es5")
    • ๐Ÿšš observable.shallowArray(values) has been removed, instead use observable.array(values, { deep: false })
    • ๐Ÿšš observable.shallowMap(values) has been removed, instead use observable.map(values, { deep: false })
    • ๐Ÿšš observable.shallowObject(values) has been removed, instead use observable.object(values, {}, { deep: false })
    • extendShallowObservable(target, props), instead use extendObservable(target, props, {}, { deep: false })
    • The decorators observable.ref, observable.shallow, observable.deep, observable.struct can no longer be used as functions. Instead, they should be passed as part of the decorators param to resp. observable.object and extendObservable
    • โšก๏ธ The new signature of extendObservable is extendObservable(target, props, decorators?, options?). This also means it is no longer possible to pass multiple bags of properties to extendObservable. extendObservable can no longer be used to re-declare properties. Use set instead to update existing properties (or introduce new ones). Update 13-01-2020: the latter limitation has been reverted in MobX 4.15.2 / 5.15.2
    • Iterating maps now follows the spec, that is, map.values(), map.entries(), map.keys(), map[@@iterator]() and array[@@iterator]() no longer return an array, but an iterator. Use mobx.values(map) or Array.from(map) to convert the iterators to arrays.
    • dropped @computed.equals, instead, you can now use @computed({ equals: ... })
    • ๐Ÿ”ง useStrict(boolean) was dropped, use configure({ enforceActions: boolean }) instead
    • ๐Ÿ”ง isolateGlobalState was dropped, use configure({ isolateGlobalState: true}) instead
    • ๐Ÿ‘€ If there are multiple mobx instances active in a single project, an exception will be thrown. Previously only a warning was printed. Fixes #1098. For details, see #1082.
    • ๐Ÿ“ฆ Dropped the shareGlobalState feature. Instead, projects should be setup properly and it is up to the hosting package to make sure that there is only one MobX instance
    • ๐Ÿšš expr has been moved to mobx-utils. Remember, expr(fn) is just computed(fn).get()
    • ๐Ÿšš createTransformer has been moved to mobx-utils
    • ๐Ÿ‘ Passing context explicitly to autorun, reaction etc is no longer supported. Use arrow functions or function.bind instead.
    • ๐Ÿšš Removed autorunAsync. Use the delay option of autorun instead.
    • ๐Ÿ‘ autorun, when, reaction don't support name as first argument anymore, instead pass the name option.
    • ๐Ÿ›  The extras. namespace has been dropped to enable tree-shaking non-used MobX features. All methods that where there originally are now exported at top level. If they are part of the official public API (you are encouraged to use them) they are exported as is. If they are experimental or somehow internal (you are discouraged to use them), they are prefixed with _.
    • ๐Ÿ›  Dropped bower support. Fixes #1263
    • The spyReportStart, spyReportEnd, spyReport and isSpyEnabled are no longer public. It is no longer possible to emit custom spy events as to avoid confusing in listeners what the possible set of events is.
    • Dropped isStrictModeEnabled
    • observable(value) will only succeed if it can turn the value into an observable data structure (a Map, Array or observable object). But it will no longer create an observable box for other values to avoid confusion. Call observable.box(value) explictly in such cases.
    • isComputed and isObservable no longer accept a property as second argument. Instead use isComputedProp and isObservableProp.
    • ๐Ÿšš Removed whyRun, use trace instead
    • The spy event signature has slightly changed
    • The Atom class is no longer exposed. Use createAtom instead (same signature).
    • Calling reportObserved() on a self made atom will no longer trigger the hooks if reportObserved is triggered outside a reactive context.
    • ๐Ÿ—„ The options struct and compareStructural for computed values are deprecated, use @computed.struct or computed({ equals: comparer.structural}) instead.
    • isModifierDescriptor is no longer exposed.
    • deepEqual is no longer exposed, use comparer.structural instead.
    • โฑ setReactionScheduler -> configure({ reactionScheduler: fn })
    • ๐Ÿ”ง reserveArrayBuffer -> configure({ reactionErrorHandler: fn })
    • ObservableMap is no longer exposed as constructor, use observable.map or isObservableMap instead
    • map -> observable.map
    • runInAction no longer accepts a custom scope
    • ๐Ÿ—„ Dropped the already deprecated and broken default export that made it harder to tree-shake mobx. Make sure to always use import { x } from "mobx" and not import mobx from "mobx".
    • ๐Ÿ‘€ Killed the already deprecated modifiers asFlat etc. If you war still using this, see the MobX 2 -> 3 migration notes.
    • ๐Ÿ‘€ Observable maps now fully implement the map interface. See #1361 by Marc Fallows
    • ๐Ÿšš Observable arrays will no longer expose the .move method
    • Dropped the observable.deep.struct modifier
    • Dropped the observable.ref.struct modifier
    • observable.struct now behaves like observable.ref.struct (this used to be observable.deep.struct). That is; values in an observable.struct field will be stored as is, but structural comparison will be used when assigning a new value
    • ๐Ÿšš IReactionDisposer.onError has been removed, use the onError option of reactions instead

    ๐Ÿš€ Issues fixed in this release:

    The issues are incoprorated in the above notes.

    • #1316 - Improve observable api
    • #992 - onBecomeObserved & onBecomeUnobserved
    • #1301 - Set onError handler when creating reactions
    • #817 - Improve typings of observe
    • #800 - Use Map as backend implementation of observable maps
    • #1361 - Make observableMaps structurally correct maps
    • ๐Ÿ— #813 - Create separate dev and production builds
    • #961, #1197 - Make it possible to forbid reading an untracked computed value
    • #1098 - Throw instead of warn if multiple MobX instances are active
    • #1122 - Atom hooks fired to often for observable maps
    • โฑ #1148 - Disposer of reactions should also cancel all scheduled effects
    • #1241 - Make it possible to disable error boundaries, to make it easier to find exceptions
    • ๐Ÿšš #1263 - Remove bower.json