- Fixed 2438, factory types caused eslint warnings, by @amareis through 2439
- Fixed 2432, array.reduce without initial value by @urugator
- Fixed 2423, array methods not dehancing by @urugator
- Fixed 2424 Map / Set instantiation triggering a strict warning, through #2425 by @moonball
-
Fixed ObservableSet.prototype.forEach not being reactive in 4.x #2341 by @melnikov-s
-
Add error when computed value declared for unspecified getter #1867 by @berrunder
-
Fixed #2326 correct array is passed to callbacks by @urugator
-
Fixed #2278,
map.delete
should respect strict mode, by @urugator -
Fixed #2253, #1980, map key order not always preserved by @urugator
-
Fixed #2412, non-enumerable getters not being picked up by
extendObservable
by @urugator -
Several performance improvements
-
Dropped
browser
fields frompackage.json
- Define action name to be as the function name #2262 by @nadavkaner
- Fixed #2230 computedvalue: throw error object instead of string when options is empty #2243 by @ramkumarvenkat
- supports ES6 Sets and Maps in shallow comparer. #2238 by @hearnden
extendObservable
: can be used existing properties again. Fixes #2250 through #2252 by @davefeucht
- Make initial values of observable set accept readonly array #2202
- Expose
_allowStateReadsStart
&_allowStateReadsEnd
. This is low level stuff you shouldn't need that's mostly useful for library creators. #2233 - Fixed an issue with
observableRequiresReaction
and updating observable during reaction #2195 - Improved type inference for
action
#2213 (see detailed explanation)
The minimum required TypeScript version is now 3.6
- Fixed flow typings with Typescript v3.6. This means that version of Typescript is required when using flows.
- Cancelled flows now reject with a
FlowCancellationError
instance whose error message is the same as in previous versions ("FLOW_CANCELLED"
) so this is not breaking. #2172 by @vonovak - Fix running mobx in web worker #2184 by @shahata
- Fixed flow typings for Facebook's Flow. A new
CancellablePromise
Flow type is exported. #2164 by @vonovak - Added support for symbol keys on observable properties (MobX 5 only). #2175 by @StephenHaney
- Fixed installation issue trying to run
postinstall
hook for a website #2165.
- Fixed a possible issue with action stack errors and multiple mobx versions installed at the same time #2135.
- Added
comparer.shallow
for shallow object/array comparisons #1561. - Fixed disposing an interception within an interception throwing an error #1950.
- Added experimental
reactionRequiresObservable
&observableRequiresReaction
config #2079, Docs - Added experimental
requiresObservable
config toreaction
,autorun
&when
options #2079, Docs
- Don't use
global
andself
keywords unless defined. Fixes #2070. - onBecome(Un)Observed didn't trigger when using number as key of observable map. Fixes #2067.
- Exposed
_startAction
and_endAction
to be able to start and action and finish it without needing a code block. This is low level stuff you shouldn't need that's mostly useful for library creators.
- Fixed potential memory leak in observable maps, when non-primitive values are used as keys. Fixes #2031 through #2032.
- Added support to store additional non-observable(!) fields (string or symbol based) on array, to better reflect behavior of MobX 4. Fixes #2044 through #2046
BREAKING CHANGE
Reverted the support of Symbols in general in MobX 4, as it gives to many potential build errors and increases the system requirements for MobX 4 (which was an oversight in 4.10.0). Apologies for the breaking change (lack of new major version numbers). If lock files are properly used however, no harm should be caused by this change.
- Reverted
Symbol
support in observable maps and objects. Reverts #1944 through #1988. Fixes #1986, #1987
- Fixed a recent regression where array update events would send undefined as
change.object
through #1985 by xaviergonz
- Added support for symbol named properties in maps and objects. Fixes #1809 and #1925 through #1944 by @loklaan
- Added
set
support forobservable.set
, see #1945 by xaviergonz - Fixed events for arrays using the wrong object, #1964 by xaviergonz
- Improved flow typings #1960 by @tbezman
- Updated tooling, #1949 and #1931 by xaviergonz
- Allow symbol keys in
ObservableMap
, see #1930 by pimterry - Fixed type definitions of
toStringTag
for Maps and Sets, see #1929 by lennerd
- Fixed regression #1878, accidental use of
Symbol
breaking Internet Explorer / React Native compatibility.
- Fixed regression in
toJS
: observable maps were not properly serialized. Fixes #1875
Features
- Introduced support for observable sets! Through #1592 by @newraina
observable.box
now accepts anequals
option, to be able to pass a custom comparision function. Through #1862, #1874 by @fi3ework. Fixes #1580- Improved logging of reactions; if an action throws an exception, errors in reactions that react to that are only logged as warnings. Fixes #1836
Fixes
- Improved typings for
flow
, see #1827 by @xaviergonz - Don't allow subclassing map, fixes #1858
- Fixed
trace(true)
not being able to handle multi-line comments in traced function. Fixes #1850 @computed
now introduces non-configurable properties, to fail fast on incorrect inheritance or property deletion. Fixes #1867- The options
enforceActions
andisolateGlobalState
now work correctly when used together. Fixes #1869
- MobX now requires TypeScript 3 (this was already the case in 5.7.0, but in this version the difference is actually noticeable in the typings).
- Fixed array dehancer sometimes skipping. Fixes #1839 through #1841 by k-g-a
- Fixed issue where webpack 4 wouldn't use the ESM module #1834 by mrtnbroder
- Improved type inference for
flow
in TypeScript 3. Fixes #1816 through #1825 by ismailhabib - Introduced support for global environment variable
IGNORE_MOBX_MINIFIY_WARNING=true
to skip the built-in minification warning. See #1835 by fi3ework - Fixed onBecome(Un)Observed dispoer cleanup. Fixes #1537 through #1833 by fi3ework
- Fixed #1839, ObservableArrayAdministration.dehanceValues does not dehance last value.
- Upgraded typings to TypeScript 3
- Fixed #1742, change detection fails when multiple mobx instances were active.
- Fixed #1624, use built-in flow types for iterators
- Fixed #1777 through #1826, stack overflow exception, in development mode, when using
@computed
on a React component. The MobX 5 behavior here has been reverted to the MobX 4 behavior.
keepAlive
has become smarter and won't recomputed computed values that are kept alive, as long as they aren't read. Implements #1534- Fixed #1796, undeleting a property that had an initial value of
undefined
was undetected - Improved Flow typings, see #1794 and #1786
- Fixed bug in
toJS
not handlingnull
values correctly. Fixes #1557 through #1783 by @wangyiz4262
toJS
now has arecurseEverything
everything option, that even detects and converts observable objects that are "behind" non-observable objects. See #1699 by wangyiz4262- Added flow typings form
comparer
, see #1751 by pdong - Update flow typings for configuration options, #1772 by alexandersorokin
(Minor version of 5
was bumped significantly to make the number better correlate together :-))
- Fixed #1740: combining decorators and
extendObservable
in a class constructor caused errors to be thrown - Fixed #1739:
- Proxies:
delete
-ing a property was not always picked up by the reactivity system - Non-proxies:
remove()
-ing a property was not always picked up by thehas()
andget()
utilities has
now returnstrue
for computed fieldsget
now returns a value for computed fields
- Proxies:
- Introduced
_allowStateChangeInsideComputed
. Don't use it :-). - MobX is now transpiled using babel 7
- Fixed #1650, decorating fields with the name
toString
does not behave correctly.
- Improved handling of multiple MobX instances. MobX will no longer complain about being loaded multiple times (one should still prevent it though, to prevent blowing up bundle size!), but merge internal state by default. If multiple MobX versions need to be loaded, call
configure({ isolateGlobalState: true })
. Note that this means that observables from the different MobX instances won't cooperate. Fixes #1681, #1082 enforceActions
options now supports the values:"never"
,"observed"
and"always"
to make the behavior more clear. Fixes #1680, #1473
- Fixed #1667: creating a large array could result in undefined items (MobX 4.* was not affected)
- Fixed #1685: expose
IAutorunOptions
decorate
now can apply multiple decorators, by accepting an array and applying them right to left:decorate(Todo, { title: [serializable(primitive), persist('object'), observable] })
. By @ramybenaroya through #1691 and #1686- Improved typings of
flow
so that it accepts async generators. By @dannsam through #1656 and #1655 keys()
now also supports arrays. Fixes #1600 through #1601 by @nunocastromartins
- Fixed issue where it was no longer possible to define custom properties on observable arrays
- Fixed issue where iterators where not compiled to ES5, breaking the ES5 based builds.
- Fixed regression bug:
ObservableMap.size
was no longer observable. Fixes #1583 - Downgraded lib export from ES6 to ES5. To many build tools still trip over ES6. Fixes #1584. A modern build is available through
import ... from "mobx/lib/mobx.es6"
(or setup an alias in your build system) - Added support for mobx-react-devtools
MobX 5 is the first MobX version fully leveraging Proxies. This has two big advantages
- MobX can now detect the addition of properties on plain observable objects, so it is now possible to use plain observable objects as dynamic collections.
- Observable arrays are now recognized as arrays by all third party libraries, which will avoid the need to slice them.
- MobX 5 can only be used on environments that support
Proxies
. See the browser support for details. - Since MobX no longer runs on older browsers, the compilation target has been upgraded to ES2015 syntax supporting browsers. This means that MobX is not loadable on older browsers without down compilation to ES5.
- If for whatever reason your project cannot meet this requirements, please stick to MobX 4. It will be actively maintained. All current features of MobX 5 are expressable in MobX 4 as well, but it means that for example to use dynamic objects some additional APIs are needed.
- The performance footprint of MobX 5 should be pretty similar to MobX 4. In our performance tests we saw some minor improvements in memory footprint, but overall it should be pretty comparable.
- The required runtime needs to support the non-polyfillable
Proxy
API. - The minimum runtime target is now ES2015, not ES5
spy
has become a no-op in production builds- All earlier deprecated APIs are dropped. Make sure to not have any deprecation warnings before upgrading.
array.move
andarray.peek
are removed from the API- Dropped the third argument to
array.find
andarray.findIndex
since they were not standardized in ES. .$mobx
property has been dropped from all observables and replaced by a Symbol. Instead of usingx.$mobx.name
, useimport { $mobx } from "mobx"; x[$mobx].name
etc.- In some cases, the order in which autoruns are fired could have changed due to some internal optimizations (note that MobX never had a guarantee about the order in which autoruns fired!)
- It is possible to pass the
proxy: false
argument toobservable.object
to disable proxying (theoretically slightly faster, but removes no dynamic key addition)
- Observable objects can no longer be frozen (otherwise they would become un-observable😎). If you are actually trying to do so MobX will now throw an exception like:
[mobx] Dynamic observable objects cannot be frozen]
. A place where that might happen unexpectedly is when passing an observable object asstyle
property to a React component. Like<span style={someObservableObject} />
, since React will freeze all style objects. The work-around is to simply pass a fresh, non-observable object for styling like:<span style={{...someObservableObject}} />
. If you are usingFixed in mobx-react 5.2.1. But note that you should still migrate away frommobx
withmobx-react
, and you are upgradingmobx-react
to the MobX 5 compatible version ([email protected]
) you will notice thatthis.props
orthis.state
are not yet observable in theconstructor
orcomponentWillMount
. This is for forward compatibility with React 16.3 wherecomponentWillMount
has been deprecated. In most cases usingcomponentDidMount
instead will suffice, especially when the goal is to setup reactions. For more info see #478.componentWillMount
😎.- Jest
toEqual
might throw an errorallKeys[x].match is not a function
when trying to equal observable arrays. This is a bug in Jest report. The simple work around for now is to slice (ortoJS
if the problem is recursive) the array first. - Jest
toEqual
matcher might no longer correctly equal your class instances, complaining about differences in the MobX adminstration. This is due to a bug with the processing of symbols: report. For now you might want to use a custom matcher if you are directly equalling observable objects. As a work aroundtoJS(object)
could be used before diffing.
Note June 7th, 2018: Both issues are already in Jest master and should be released soon.
- Make sure to not use any API that produces deprecation warnings in MobX 4. Beyond that MobX 5 should pretty well as drop-in replacement of MobX 4.
- You could perform the following clean ups:
- Don't
slice()
arrays when passing them to external libraries. (Note you still shouldn't pass observable data structures to non-observer
React components, which is an orthogonal concept) - You could replace observable maps with observable objects if you are only using string-based keys.
- Don't
- Don't call the
reverse
orsort
operations directly on observableArray's anymore, as it's behavior slightly differed from the built-in implementations of those methods. Instead useobservableArray.slice().sort()
to perform the sort on a copy. This gives no additional performance overhead compared to MobX 4. (The reason behind this is that built-insort
updates the array in place, but the observable array implementation always performed the sort on a defensive copy, and this change makes that explicit). - you may remove usages of
isArrayLike()
sinceArray.isArray()
will now return true for observable arrays
- The
arrayBuffer
setting is no longer supported byconfigure
(it has become irrelevant) observable.shallowBox
,observable.shallowArray
,observable.shallowMap
,observable.shallowObject
,extendShallowObservable
api's have been removed. Instead, pass{ deep: false }
to their non-shallow counter parts.observableArray.peek
,observableArray.move
- Fixed [#1534](Fixes mobxjs/mobx#1534): @computed({keepAlive: true}) no long calculates before being accessed.
- Added the
$mobx
export symbol for MobX 5 forward compatibity
- Introduced the
entries(observable)
API, by @samjacobclift through #1536 - Fixed #1535: Change in nested computed value was not propagated if read outside action context when there is a pending reaction. For more details see the exact test case.
- Illegal property access through prototypes is now a warning instead of an error. Fixes #1506. By @AmazingTurtle through #1529
- Fixed issue where providing a custom setter to
@computed({ set: ... })
wasn't picked up - Fixed #1545: Actions properties where not re-assignable when using TypeScript
- Illegal Access checks are now a warning instead of an error. Fix
- Fixed flow typings for
mobx.configure
#1521 by @andrew--r - Improved typings for
mobx.flow
, fixes #1527 - Throw error when using
@observable
in combination with a getter. #1511 by @quanganhtran toJS
now uses Map internally, for faster detection of cycles. #1517 by @loatheb- Fixed #1512:
observe
hooks not being triggered when usingmobx.set
, Fixed in #1514 by @quanganhtran - Several minor improvements, additional tests and doc improvements.
- Introduced
configure({ enforceActions: "strict" })
, which is more strict thenenforceActions: true
, as it will also throw on non-observed changes to observables. See also #1473 - Fixed #1480: Exceptions in the effect handler of
reaction
where not properly picked up by the global reaction system - Fixed a bug where computed values updated their cached value, even when the comparer considered the new value equal to the previous one. Thanks @kuitos for finding this and fixing it! #1499
- Undeprecated
ObservableMap
, fixes #1496 - Observable arrays now support
Symbol.toStringTag
(if available / polyfilled). This allows libraries like Ramda to detect automatically that observable arrays are arrays. Fixes #1490. Note thatArray.isArray
will keep returning false for the entire MobX 4 range. - Actions are now always
configurable
andwritable
, like in MobX 3. Fixes #1477 - Merged several improvements to the flow typings. #1501 by @quanganhtran
- Fixed several accidental usages of the global
fail
, by @mtaran-google through #1483 and #1482
- Import
default
from MobX will no longer throw, but only warn instead. This fixes some issues with tools that reflect on thedefault
export of a module - Disposing a spy listener inside a spy handler no longer causes an exception. Fixes #1459 through #1460 by farwayer
- Added a missing
runInAction
overload in the flow typings. #1451 by AMilassin - Improved the typings of
decorate
. See #1450 by makepost
- Introduced
keepAlive
as option tocomputed
- All observable api's now default to
any
for their generic arguments - Improved
flow
cancellation - The effect of
when
is now automatically an action. @computed
properties are now declared on their owner rather then the protoptype. Fixes an issue where@computed
fields didn't work in React Native on proxied objects. See #1396action
andaction.bound
decorated fields are now reassignable, so that they can be stubbed
- Fixed issue where exceptions like
TypeError: Cannot define property:__mobxDidRunLazyInitializers, object is not extensible.
were thrown. Fixes #1404 - Improved flow typings for
flow
, #1399 by @ismailhabib
- 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.
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 asasyncActions
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
andonBecomeUnobserved
. 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 arequiresReaction
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, usemobx.configure({ computedRequiresReaction: true })
-
Introduced
mobx.configure({ disableErrorBoundaries })
, for easier debugging of exceptoins. By NaridaL through #1262 -
toJS
now accepts the options:{ detectCycles?: boolean, exportMapsAsObjects?: boolean }
, bothtrue
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 propertyname: "debug name"
equals: fn
the equality value to use for the computed to determine whether its output has changed. The default iscomparer.default
. Alternatives arecomparer.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 apiautorunAsync
name: "debug name"
scheduler: function
a custom scheduler to run the autorun. For example to connect running the autorun torequestAnimationFrame
. See the docs for more detailsonError
. 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 apiautorunAsync
fireImmediately
. Immediately fire the effect function after the first evaluation ofexpr
equals
. Custom equality function to determine whether theexpr
function differed from its previous result, and hence should fireeffect
. Accepts the same options as theequals
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 theonError
handler will be triggered to signal the condition not being met within a certain time
-
The
effect
parameter ofwhen
has become optional. If it is omitted,when
will return a promise. This makes it easy toawait
a condition, for example:await when(() => user.profile.loaded)
. The returned promise can be cancelled usingpromise.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, andvalues
orkeys
to iterate them.values(thing)
returns all values in the collection as arraykeys(thing)
returns all keys in the collection as arrayset(thing, key, value)
orset(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
andextendObservable
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 toobservable.ref
,observable.struct
etc. (Thedeep
property is just a short-hand for switching betweenobservable.deep
orobservable.ref
as default decorator for new properties)
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 severalSymbol
s to exist for its typings. So make sure that thelib
configuration of your project is set to"es6"
. (The compilation target can still be"es5"
) observable.shallowArray(values)
has been removed, instead useobservable.array(values, { deep: false })
observable.shallowMap(values)
has been removed, instead useobservable.map(values, { deep: false })
observable.shallowObject(values)
has been removed, instead useobservable.object(values, {}, { deep: false })
extendShallowObservable(target, props)
, instead useextendObservable(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 thedecorators
param to resp.observable.object
andextendObservable
- The new signature of
extendObservable
isextendObservable(target, props, decorators?, options?)
. This also means it is no longer possible to pass multiple bags of properties toextendObservable
.Update 13-01-2020: the latter limitation has been reverted in MobX 4.15.2 / 5.15.2extendObservable
can no longer be used to re-declare properties. Useset
instead to update existing properties (or introduce new ones). - Iterating maps now follows the spec, that is,
map.values()
,map.entries()
,map.keys()
,map[@@iterator]()
andarray[@@iterator]()
no longer return an array, but an iterator. Usemobx.values(map)
orArray.from(map)
to convert the iterators to arrays. - dropped
@computed.equals
, instead, you can now use@computed({ equals: ... })
useStrict(boolean)
was dropped, useconfigure({ enforceActions: boolean })
insteadisolateGlobalState
was dropped, useconfigure({ 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 justcomputed(fn).get()
createTransformer
has been moved to mobx-utils- Passing
context
explicitly toautorun
,reaction
etc is no longer supported. Use arrow functions or function.bind instead. - Removed
autorunAsync
. Use thedelay
option ofautorun
instead. autorun
,when
,reaction
don't support name as first argument anymore, instead pass thename
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
andisSpyEnabled
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. Callobservable.box(value)
explictly in such cases.isComputed
andisObservable
no longer accept a property as second argument. Instead useisComputedProp
andisObservableProp
.- Removed
whyRun
, usetrace
instead - The spy event signature has slightly changed
- The
Atom
class is no longer exposed. UsecreateAtom
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
andcompareStructural
for computed values are deprecated, use@computed.struct
orcomputed({ equals: comparer.structural})
instead. isModifierDescriptor
is no longer exposed.deepEqual
is no longer exposed, usecomparer.structural
instead.setReactionScheduler
->configure({ reactionScheduler: fn })
reserveArrayBuffer
->configure({ reactionErrorHandler: fn })
ObservableMap
is no longer exposed as constructor, useobservable.map
orisObservableMap
insteadmap
->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 useimport { x } from "mobx"
and notimport 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 likeobservable.ref.struct
(this used to beobservable.deep.struct
). That is; values in anobservable.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
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
- Fixed #1118, the deepEquals operator build into mobx gave wrong results for non-primitive objects. This affected for example
computed.struct
, or thecompareStructural
ofreaction
- Introduced
trace
for easier debugging of reactions / computed values. See the docs for details. - Improved typings of
observableArray.find
, see #1324 for details.
- Republished 3.4.0, because the package update doesn't seem to distibute consistently through yarn / npm
- Improve Flow support by exposing typings regularly. Flow will automatically include them now. In your
.flowconfig
will have to remove the import in the[libs]
section (as it's done here). Fixes #1232.
- Fixed regression bug where observable map contents could not be replaced using another observable map #1258
- Fixed weird exception abot not being able to read
length
property of a function, see#1238 through #1257 by @dannsam
- Fix bug where custom comparers could be invoked with
undefined
values. Fixes #1208 - Make typings for observable stricter when using flow #1194, #1231
- Fix a bug where
map.replace
would trigger reactions for unchanged values, fixes #1243 - Fixed issue where
NaN
was considered unequal toNaN
when a deep compare was made #1249
- Fix bug allowing maps to be modified outside actions when using strict mode, fixes #940
- Fixed #1139 properly:
transaction
is no longer deprecated and doesn't disable tracking properties anymore - Fixed #1120:
isComputed
should return false for non-existing properties
- Undeprecated
transaction
, see #1139 - Fixed typings of reaction #1136
- It is now possible to re-define a computed property #1121
- Print an helpful error message when using
@action
on a getter #971 - Improved typings of intercept #1119
- Made code base Prettier #1103
- react-native will now by default use the es module build as well.
- Added support for Weex, see #1163
- Added workaround for Firefox issue causing MobX to crash, see #614
- Fixes a bug (or a known limitation) described in #1092. It is now possible to have different observable administration on different levels of the prototype chain. By @guillaumeleclerc
- Fixed a build issue when using mobx in a project that was using rollup, fixes #1099 by @rossipedia
- Fixed typings of
useStrict
, by @rickbeerendonk
- Introduced customizable value comperators to reactions and computed values.
reaction
andcomputed
now support an additional option,equals
, which takes a comparision function. See #951 by @jamiewinder. Fixes #802 and #943. See the updatedcomputed
docs for more details.
- MobX will warn again when there are multiple instances of MobX loaded, as this lead to often to confusing bugs if the project setup was not properly. The signal mobx that multiple instances are loaded on purpose, use
mobx.extras.runInSandbox
. See #1082 for details.
- Improved typings of
IObservableArray.intercept
: use more restrictive types forchange
parameter ofhandler
, by @bvanreeven - Fixed #1072, fields without a default value could not be observed yet when using TypeScript
- Restored
default
export (and added warning), which broke code that was importing mobx likeimport mobx from "mobx"
. Useimport * as mobx from "mobx"
or use named importes instead. By @andykog, see #1043, #1050 - Fixed several typos in exceptions and documentation
- Fixed issue where
array.remove
did not work correctly in combination withextras.interceptReads
- Fixed 3.1.12 / 3.1.13 module packing. See #1039;
module
target is now transpiled to ES5 as well
- Fixed build issue with webpack 2, see #1040
- Added support for ES modules. See #1027 by @rossipedia
- Improved flow typings. See #1019 by @fb55
- Introduced experimental feature
extras.interceptReads(observable: ObservableMap | ObservableArray | ObservableObject | ObservableBox, property?: string, handler: value => value): Disposer
that can be used to intercept reads from observable objects, to transform values on the fly when a value is read. One can achieve similar things with this as with proxying reads. See #1036
- Using rollup as bundler, instead of custom hacked build scripts, by @rossipedia, see #1023
- Fixed flow typings for
when
, by @jamsea - Add flow typings for
map.replace
, by @leader22 - Added
observableArray.findIndex
, by @leader22 - Improved typings of
autorun
/autorunAsync
to better support async / await, by @capaj - Fixed typings of
action.bound
, see #803
- Introduced explicit
.get(index)
and.set(index, value)
methods on observable arrays, for issues that have trouble handling many property descriptors on objects. See also #734 - Made sure it is safe to call
onBecomeObserved
twice in row, fixes #874, #898 - Fixed typings of
IReactionDisposer
- Fixed edge case where
autorun
was not triggered again if a computed value was invalidated by the reaction itself, see #916, by @andykog - Added support for primtive keys in
createTransformer
, See #920 by @dnakov - Improved typings of
isArrayLike
, see #904, by @mohsen1
- Reverted ES2015 module changes, as they broke with webpack 2 (will be re-released later)
- Expose ES2015 modules to be used with advanced bundlers, by @mohsen1, fixes #868
- Improved typings of
IObservableArray.intercept
: remove superflous type parameter, by @bvanreeven - Improved typings of map changes, by @hediet
- Improved typings of map changes, see #847, by @hediet
- Fixed issue with
reaction
iffireImmediately
was combined withdelay
option, see #837, by @SaboteurSpk
- Observable maps initialized from ES6 didn't deeply convert their values to observables. (fixes #869,by @ggarek)
- Make sure that
ObservableArray.replace
can handle large arrays by not using splats internally. (See e.g. #859) - Exposed
ObservableArray.spliceWithArray
, that unlike a normal splice, doesn't use a variadic argument list so that it is possible to splice in new arrays that are larger then allowed by the callstack.
- Fixed incompatiblity issue with
[email protected]
- Introduced
isBoxedObservable(value)
, fixes #804
Strict mode has been relaxed a bit in this release. Also computed values can now better handle creating new observables (in an action if needed). The semantics are now as follows:
- In strict mode, it is not allowed to modify state that is already being observed by some reaction.
- It is allowed to create and modify observable values in computed blocks, as long as they are not observed yet.
In order words: Observables that are not in use anywhere yet, are not protected by MobX strict mode. This is fine as the main goal of strict mode is to avoid kicking of reactions at undesired places. Also strict mode enforces batched mutations of observables (through action). However, for unobserved observables this is not relevant; they won't kick of reactions at all.
This fixes some uses cases where one now have to jump through hoops like:
- Creating observables in computed properties was fine already, but threw if this was done with the aid of an action. See issue #798.
- In strict mode, it was not possible to update observable values without wrapping the code in
runInAction
oraction
. See issue #563
Note that the following constructions are still anti patterns, although MobX won't throw anymore on them:
- Changing unobserved, but not just created observables in a computed value
- Invoke actions in computed values. Use reactions like
autorun
orreaction
instead.
Note that observables that are not in use by a reaction, but that have .observe
listeners attached, do not count towards being observed.
Observe and intercept callbacks are concepts that do not relate to strict mode, actions or transactions.
- Reactions and observable values now consider
NaN === NaN
, See #805 by @andykog - Merged #783: extract error messages to seperate file, so that they can be optimized in production builds (not yet done), by @reisel, #GoodnessSquad
- Improved typings of actions, see #796 by @mattiamanzati
- Fixed issue where MobX failed on environments where
Map
is not defined, #779 by @dirtyrolf - MobX can now be compiled on windows as well! #772 by @madarauchiha #GoodnessSquad
- Added documentation on how Flow typings can be used, #766 by @wietsevenema
- Added support for
Symbol.toPrimitive()
andvalueOf()
, see #773 by @eladnava #GoodnessSquad - Supressed an exception that was thrown when using the Chrome Developer tools to inspect arrays, see #752
Re-introduced structural comparison. Seems we couldn't part from it yet :). So the following things have been added:
struct
option toreaction
(alias forcompareStructural
, to get more consistency in naming)observable.struct
, as alias forobservable.deep.struct
observable.deep.struct
: Only stores a new value and notify observers if the new value is not structurally the same as the previous value. Beware of cycles! Converts new values automatically to observables (likeobservable.deep
)observable.ref.struct
: Only stores a new value and notify observers if the new value is not structurally the same as the previous value. Beware of cycles! Doesn't convert the new value into observables.extras.deepEquals
: Check if two data structures are deeply equal. supports observable and non observable data structures.
toString()
of observable arrays now behaves like normal arrays (by @capaj, see #759)- Improved flow types of
toJS
by @jamsea (#758)
The changelog of MobX 3 might look quite overwhelming, but migrating to MobX 3 should be pretty straight forward nonetheless.
The api has now become more layered, and the api is more uniform and modifiers are cleaned up.
In practice, you should check your usage of modifiers (asFlat
, asMap
etc.). Besides that the migration should be pretty painless.
Please report if this isn't the case!
Note that no changes to the runtime algorithm where made, almost all changes evolve in making the creation of observables more uniform, and removing deprecated stuff.
The api to create observables has been redesigned. By default, it keeps the automatic conversion behavior from MobX 2. However, one can now have more fine grained control on how / which observables are constructed. Modifiers still exists, but they are more regular, and there should be less need for them.
When passing a plain object to observable
, MobX used to modify that object inplace and give it observable capabilities.
This also happened when assigning a plain object to an observable array etc.
However, this behavior has changed for a few reasons
- Both arrays and maps create new data structure, however,
observable(object)
didn't - It resulted in unnecessary and confusing side effects. If you passed an object you received from some api to a function that added it, for example, to an observable collection. Suddenly your object would be modified as side effect of passing it down to that function. This was often confusing for beginners and could lead to subtle bugs.
- If MobX in the future uses Proxies behind the scenes, this would need to change as well
If you want, you can still enhance existing plainObjects, but simply using extendObservable(data, data)
. This was actually the old implementation, which has now changed to extendObservable({}, data)
.
As always, it is best practice not to have transportation objects etc lingering around; there should be only one source of truth, and that is the data that is in your observable state. If you already adhered to this rule, this change won't impact you.
See #649
There are now explicit methods to create an observable of a specific type.
observable.object(props, name?)
creates a new observable object, by cloning the give props and making them observableobservable.array(initialValues, name?)
. Take a guess..observable.map(initialValues, name?)
observable.box(initialValue, name?)
. Creates a boxed value, which can be read from / written to using.get()
and.set(newValue)
observable(value)
, as-is, based on the type ofvalue
, uses any of the above four functions to create a new observable.
The standard observable factories create observable structures that will try to turn any plain javascript value (arrays, objects or Maps) into observables.
Allthough this is fine in most cases, in some cases you might want to disable this autoconversion.
For example when storing objects from external libraries.
In MobX 2 you needed to use asFlat
or asReference
modifiers for this.
In MobX 3, there are factories to directly create non-converting data structures:
observable.shallowObject(props, name?)
observable.shallowArray(initialValues, name?)
observable.shallowMap(initialValues, name?)
observable.shallowBox(initialValue, name?)
So for example, observable.shallowArray([todo1, todo2])
will create an observable array, but it won't try to convert the todos inside the array into observables as well.
The @observable
decorator can still be used to introduce observable properties. And like in MobX 2, it will automatically convert its values.
However, sometimes you want to create an observable property that does not convert its value into an observable automatically.
Previously that could be written as @observable x = asReference(value)
.
This was not for a technical reason, but they just seemed hardly used.
Structural comparision for computed properties and reactions is still possible.
Feel free to file an issue, including use case, to re-introduce this feature if you think you really need it.
However, we noticed that in practice people rarely use it. And in cases where it is used reference
/ shallow
is often a better fit (when using immutable data for example).
Modifiers can be used in combination @observable
, extendObservable
and observable.object
to change the autoconversion rules of specific properties.
The following modifiers are available:
observable.deep
: This is the default modifier, used by any observable. It converts any assigned, non-primitive value into an observable value if it isn't one yet.observable.ref
: Disables automatic observable conversion, just creates an observable reference instead.observable.shallow
: Can only used in combination with collections. Turns any assigned collection into an collection, which is shallowly observable (instead of deep)
Modifiers can be used as decorator:
class TaskStore {
@observable.shallow tasks = []
}
Or as property modifier in combination with observable.object
/ observable.extendObservable
.
Note that modifiers always 'stick' to the property. So they will remain in effect even if a new value is assigned.
const taskStore = observable({
tasks: observable.shallow([])
})
See modifiers
Using computed
to create boxed observables has been simplified, and computed
can now be invoked as follows:
computed(expr)
computed(expr, setter)
computed(expr, options)
, where options is an object that can specify one or more of the following fields:name
,setter
,compareStructural
orcontext
(the "this").
Computed can also be used as a decorator:
@computed
@computed.struct
when you want to compareStructural (previously was@computed({asStructure: true})
)
The signature of reaction
is now reaction(dataFunc, effectFunc, options?)
, where the following options are accepted:
context
: Thethis
to be used in the functionsfireImmediately
delay
: Number in milliseconds that can be used to debounce the effect function.compareStructural
:false
by default. Iftrue
, the return value of the data function is structurally compared to its previous return value, and the effect function will only be invoked if there is a structural change in the output.name
: String
It is now possible to create actions and bind them in one go using action.bound
. See #699.
This means that now the following is possible:
class Ticker {
@observable tick = 0
@action.bound
increment() {
this.tick++ // 'this' will always be correct
}
}
const ticker = new Ticker()
setInterval(ticker.increment, 1000)
Error handling in MobX has been made more consistent. In MobX 2 there was a best-effort recovery attempt if a derivation throws, but MobX 3 introduced more consistent behavior:
- Computed values that throw, store the exception and throw it to the next consumer(s). They keep tracking their data, so they are able to recover from exceptions in next re-runs.
- Reactions (like
autorun
,when
,reaction
,render()
ofobserver
components) will always catch their exceptions, and just log the error. They will keep tracking their data, so they are able to recover in next re-runs. - The disposer of a reaction exposes an
onError(handler)
method, which makes it possible to attach custom error handling logic to an reaction (that overrides the default logging behavior). extras.onReactionError(handler)
can be used to register a global onError handler for reactions (will fire after spy "error" event). This can be useful in tests etc.
See #731
MobX always printed a warning when an exception was thrown from a computed value, reaction or react component: [mobx] An uncaught exception occurred while calculating....
.
This warning was often confusing for people because they either had the impression that this was a mobx exception, while it actually is just informing about an exception that happened in userland code.
And sometimes, the actual exception was silently caught somewhere else.
MobX now does not print any warnings anymore, and just makes sure its internal state is still stable.
Not throwing or handling an exception is now entirely the responsibility of the user.
Throwing an exception doesn't revert the causing mutation, but it does reset tracking information, which makes it possible to recover from exceptions by changing the state in such a way that a next run of the derivation doesn't throw.
Flow typings have been added by A-gambit. Add flow types for methods and interfaces of observable variables:
const observableValue: IObservableValue<number> = observable(1)
const observableArray: IObservableArray<number> = observable([1, 2, 3])
const sum: IComputedValue<number> = computed(() => {
return observableArray.reduce((a: number, b: number): number => a + b, 0)
})
See #640
For historical reasons (at Mendix), MobX had a feature that it would warn if different versions of the MobX package are being loaded into the same javascript runtime multiple times. This is because multiple instances by default try to share their state. This allows reactions from one package to react to observables created by another package, even when both packages where shipped with their own (embedded) version of MobX (!).
Obviously this is a nasty default as it breaks package isolation and might actually start to throw errors unintentionally when MobX is loaded multiple times in the same runtime by completely unrelated packages. So this sharing behavior is now by default turned off. Sharing MobX should be achieved by means of proper bundling, de-duplication of packages or using peer dependencies / externals if needed. This is similar to packages like React, which will also bail out if you try to load it multiple times.
If you still want to use the old behavior, this can be achieved by running mobx.extras.shareGlobalState()
on all packages that want to share state with each other.
Since this behavior is probably not used outside Mendix, it has been deprecated immediately, so if you rely on this feature, please report in #621, so that it can be undeprecated if there is no more elegant solution.
See #621
Don't use the @action
decorator on an individual object that you pass to observable()
or extendObservable()
. If you have code that looks like observable({ @action f: () => {})
, you should change it to observable({ f: action(() => {})
.
Whether or not this was ever a good idea is debatable, but it stopped working in this version. If you're using classes, it's a non-issue.
- Breaking change: The arguments to
observe
listeners for computed and boxed observables have changed and are now consistent with the other apis. Instead of invoking the callback with(newValue: T, oldValue: T)
they are now invoked with a single change object:(change: {newValue: T, oldValue: T, object, type: "update"})
- Using transaction is now deprecated, use
action
orrunInAction
instead. Transactions now will enter anuntracked
block as well, just as actions, which removes the conceptual difference. - Upgraded to typescript 2
- It is now possible to pass ES6 Maps to
observable
/ observable maps. The map will be converted to an observable map (if keys are string like) - Made
action
more debug friendly, it should now be easier to step through - ObservableMap now has an additional method,
.replace(data)
, which is a combination ofclear()
andmerge(data)
. - Passing a function to
observable
will now create a boxed observable refering to that function - Fixed #603: exceptions in transaction breaks future reactions
- Fixed #698: createTransformer should support default arguments
- Transactions are no longer reported grouped in spy events. If you want to group events, use actions instead.
- Normalized
spy
events further. Computed values and actions now reportobject
instead oftarget
for the scope they have been applied to. - The following deprecated methods have been removed:
transaction
autorunUntil
trackTransitions
fastArray
SimpleEventEmitter
ObservableMap.toJs
(usetoJS
)toJSlegacy
toJSON
(usetoJS
)- invoking
observe
andinject
with plain javascript objects
A deprecation message will now be printed if creating computed properties while relying on automatical inferrence of argumentless functions as computed values. In other words, when using observable
or extendObservable
in the following manner:
const x = observable({
computedProp: function() {
return someComputation
}
})
// Due to automatic inferrence now available as computed property:
x.computedProp
// And not !
x.computedProp()
Instead, to create a computed property, use:
observable({
get computedProp() {
return someComputation
}
})
or alternatively:
observable({
computedProp: computed(function() {
return someComputation
})
})
This change should avoid confusing experiences when trying to create methods that don't take arguments. The current behavior will be kept as-is in the MobX 2.* range, but from MobX 3 onward the argumentless functions will no longer be turned automatically into computed values; they will be treated the same as function with arguments. An observable reference to the function will be made and the function itself will be preserved. See for more details #532
N.B. If you want to introduce actions on an observable that modify its state, using action
is still the recommended approach:
observable({
counter: 0,
increment: action(function() {
this.counter++
})
})
By the way, if you have code such as:
observable({
@computed get someProp() { ... }
});
That code will no longer work. Rather, reactions will fail silently. Remove @computed
.
Note, this only applies when using observable in this way; it doesn't apply when using
@observable
on a property within a class declaration.
- Fixed #701:
toJS
sometimes failing to convert objects decorated with@observable
(cause:isObservable
sometimes returned false on these object) - Fixed typings for
when
/autorun
/reaction
; they all return a disposer function.
- Added
move
operation to observable array, see #697
- Fixed potential clean up issue if an exception was thrown from an intercept handler
- Improved typings of
asStructure
(by @nidu, see #687) - Added support for
computed(asStructure(() => expr))
(by @yotambarzilay, see #685)
- Fixed #603: exceptions in transaction breaks future reactions
- Improved typings of
toJS
- Introduced
setReactionScheduler
. Internal api used by mobx-react@4 to be notified when reactions will be run
- Changes related to
toJS
as mentioned in version2.6.0
where not actually shipped. This has been fixed, so see release notes below.
- Introduced convenience
isArrayLike
: returns whether the argument is either a JS- or observable array. By @dslmeinte - Improved readme. By @DavidLGoldberg
- Improved assertion message, by @ncammarate (See #618)
- Added HashNode badge, by @sandeeppanda92
Marked as minor release as the behavior of toJS
has been changed, which might be interpreted both as bug-fix or as breaking change, depending of how you interpreted the docs
- Fixed #566: Fixed incorrect behavior of
toJS
:toJS
will now only recurse into observable object, not all objects. The new behavior is now aligned with what is suggested in the docs, but as a result the semantics changed a bit.toJSlegacy
will be around for a while implementing the old behavior. See [#589](See mobxjs/mobx#589) for more details. - Fixed #571: Don't use
instanceof
operator. Should fix issues if MobX is included multiple times in the same bundle. - Fixed #576: disallow passing actions directly to
autorun
; as they won't be tracked by @jeffijoe - Extending observable objects with other observable (objects) is now explicitly forbidden, fixes #540.
- Introduced
isComputed
- Observable objects can now have a type:
IObservableObject
, see #484 by @spiffytech - Restored 2.4 behavior of boxed observables inside observable objects, see #558
- Computed properties can now be created by using getter / setter functions. This is the idiomatic way to introduce computed properties from now on:
const box = observable({
length: 2,
get squared() {
return this.length * this.length
},
set squared(value) {
this.length = Math.sqrt(value)
}
})
- Core derivation algorithm has received some majore improvements by @asterius1! See below. Pr #452, 489
- Introduced setters for computed properties, use
computed(expr, setter)
or@computed get name() { return expr } set name (value) { action }
.computed
can now be used as modifier inobservable
/extendObservable
, #421, #463 (see below for example) - Introduced
isStrictModeEnabled()
, deprecateduseStrict()
without arguments, see #464 - Fixed #505, accessing an observable property throws before it is initialized
MobX is now able track and memoize computed values while an (trans)action is running. Before 2.5, accessing a computed value during a transaction always resulted in a recomputation each time the computed value was accessed, because one of the upstream observables (might) have changed. In 2.5, MobX actively tracks whether one of the observables has changed and won't recompute computed values unnecessary. This means that computed values are now always memoized for the duration of the current action. In specific cases, this might signficantly speed up actions that extensively make decisions based on computed values.
Example:
class Square {
@observable length = 2
@computed get squared() {
return this.length * this.length
}
// mobx now supports setters for computed values
set squared(surfaceSize) {
this.length = Math.sqrt(surfaceSize)
}
// core changes make actions more efficient if extensively using computed values:
@action stuff() {
this.length = 3
console.log(this.squared) // recomputes in both 2.5 and before
console.log(this.squared) // no longer recomputes
this.length = 4
console.log(this.squared) // recomputes in both 2.5 and before
// after the action, before 2.5 squared would compute another time (if in use by a reaction), that is no longer the case
}
}
ES5 example for setters:
function Square() {
extendObservable(this, {
length: 2,
squared: computed(
function() {
return this.squared * this.squared
},
function(surfaceSize) {
this.length = Math.sqrt(surfaceSize)
}
)
})
}
- Fixed #503: map.delete returns boolean
- Fix return type of
runInAction
, #499 by @Strate - Fixed enumerability of observable array methods, see #496.
- Use TypeScript typeguards, #487 by @Strate
- Added overloads to
action
for better type inference, #500 by @Strate - Fixed #502:
extendObservable
fails on objects created withObject.create(null)
- Implemented #480 / #488: better typings for
asMap
, by @Strate
- Objects with a
null
prototype are now considered plain objects as well - Improved error message for non-converging cyclic reactions
- Fixed potential HMR issue
- Improved error message when wrongly using
@computed
, by @bb (#450) observableArray.slice
now automatically converts observable arrays to plain arrays, fixes #460- Improved error message when an uncaught exception is thrown by a MobX tracked function
@action
decorated methods are now configurable. Fixes #441- The
onBecomeObserved
event handler is now triggered when an atom is observed, instead of when it is bound as dependency. Fixes #427 and makes atoms easier to extend. - if
useStrict()
is invoked without arguments, it now returns the current value of strict mode. - the current reaction is now always passed as first argument to the callbacks of
autorun
,autorunAsync
,when
andreaction
. This allows reactions to be immediately disposed during the first run. See #438, by @andykog
- Note: the internal version of MobX has been bumped. This version has no breaking api changes, but if you have MobX loaded multiple times in your project, they all have to be upgraded to
2.4.0
. MobX will report this when starting. - Made dependency tracking and binding significant faster. Should result in huge performance improvements when working with large collections.
- Fixed typescript decorator issue, #423, #425? (by @bb)
- Fixed issue where computed values were tracked and accidentally kept alive during actions
- Fixed #406: Observable maps doesn't work with empty initial value in Safari
- Implemented #357, #348: ObservableMap and ObservableArray now support iterators. Use
@@iterator()
or iterall in ES5 environments.
- Fixed #364: Observable arrays not reacting properly to index assignments under iOS safari (mobile) 9.1.1 By @andykog
- Fixed #387: Typings of boxed values
- Added warning when reading array entries out of bounds. See #381
- Fixed #360: Removed expensive cycle detection (cycles are still detected, but a bit later)
- Fixed #377:
toJS
serialization of Dates and Regexes preserves the original values - Fixed #379:
@action
decorated methods can now be inherited / overriden
- Fixed #186: Log a warning instead of an error if an exception is thrown in a derivation. Fixes issue where React Native would produce unusable error screens (because it shows the first logged error)
- Fixed #333: Fixed some interoperability issues in combination with
Reflect
/InversifyJS
decorators. @andykog - Fixed #333:
@observable
class properties are now owned by their instance again, meaning they will show up inObject.keys()
and.hasOwnProperty
@andykog
- Fixed #328: Fixed exception when inspecting observable in
onBecomeObserved
- Fixed #341:
array.find
now returnsundefined
instead ofnull
when nothing was found, behavior now matches the docs. (By @hellectronic)
- Fixed #327: spy not working with runInAction
Usage:
whyRun()
whyRun(Reaction object / ComputedValue object / disposer function)
whyRun(object, "computed property name")
whyRun
is a small utility that can be used inside computed value or reaction (autorun
, reaction
or the render
method of an observer
React component)
and prints why the derivation is currently running, and under which circumstances it will run again.
This should help to get a deeper understanding when and why MobX runs stuff, and prevent some beginner mistakes.
This feature can probably be improved based on your feedback, so feel free to file issues with suggestions!
@observable
is now always defined on the class prototypes and not in the instances. This means that@observable
properties are enumerable, but won't appear ifObject.keys
orhasOwnProperty
is used on a class instance.- Updated semantics of
reaction
as discussed in#278
. The expression now needs to return a value and the side effect won't be triggered if the result didn't change.asStructure
is supported in these cases. In contrast to MobX 2.2, effects will no longer be run if the output of the expression didn't change.
- Introduces
isAction(fn)
#290 - If an (argumentless) action is passed to
observable
/extendObservable
, it will not be converted into a computed property. - Fixed #285: class instances are now also supported by
toJS
. Also members defined on prototypes which are enumerable are converted. - Map keys are now always coerced to strings. Fixes #308
when
,autorun
andautorunAsync
now accept custom debug names (see #293, by @jamiewinder)- Fixed #286: autoruns no longer stop working if an action throws an exception
- Implemented
runInAction
, can be used to create on the fly actions (especially useful in combination withasync/await
, see #299 - Improved performance and reduced mem usage of decorators signficantly (by defining the properties on the prototype if possible), and removed subtle differences between the implementation and behavior in babel and typescript.
- Updated logo as per #244. Tnx @osenvosem!
- Fixed issue #267: exception when
useStrict(true)
was invoked in combination with@observable
attributes when using Babel - Fixed issue #269: @action in combination with typescript targeting ES6 and reflect.ts
- Improved compatibility with
JSON.stringify
, removed incorrect deprecation message - Improved some error messages
- Fixed issue where typescript threw a compile error when using
@action
without params on a field - Fixed issue where context was accidentally shared between class instances when using
@action
on a field
See the release announcement for the full details of this release:
Introduced:
action
/@action
intercept
spy
reaction
useStrict
- improved debug names
toJSON
was renamed totoJS
observable(asMap())
is the new idiomatic way to create maps- the effect of
when
is now untracked, similar to `reaction. extras.trackTransations
is deprecated, usespy
insteaduntracked
has been undeprecated- introduced / documented:
getAtom
,getDebugName
,isSpyEnabled
,spyReport
,spyReportStart
,spyReportEnd
- deprecated
extras.SimpleEventEmitter
- array splice events now also report the
added
collection andremovedCount
- Fixed a false negative in cycle detection, as reported in #236
- Fixed #236, #237 call stack issues when working with large arrays
- Fix #222 (by @andykog) run
observe
callback of computed properties in untracked mode.
- Fixed #201 (see also #160), another iOS enumerability issue... By @luosong
- Fixed #191, when using babel, complex field initializers where shared. By @andykog
- Added
lib/mobx.umd.min.js
for minified cdn builds, see #85
- Improved debug names of objects created using a constructor
- Fixed(?) some issues with iOS7 as reported in #60 by @bstst
- Fixed issue where
autorun
s created insideautorun
s were not always kicked off. (mobx-react
'sobserver
was not affected). Please upgrade if you often use autorun. - Fixed typings of
mobx.map
, a list of entries is also acceptable. - (Experimental) Improved error recovery a bit further
- MobX is now chatty again when an exception occurs inside a autorun / computed value / React.render. Previously this was considered to be the responsibility of the surrounding code. But if exceptions were eaten this would be really tricky to debug.
- (Experimental) MobX will now do a poor attempt to recover from exceptions that occured in autorun / computed value / React.render.
resetGlobalState
is now part of themobx.extras
namespace, as it is useful for test setup, to restore inconsistent state after test failures.resetGlobalState
now also resets the caches ofcreateTransformer
, see #163.
- WIP on bower support
$transformId
property on transformed objects should be non-enumerable. Fixes #170.
- Always peek if inspecting a stale, computed value. Fixes #165.
- Fixed issue where changing an object property was tracked, which could lead to unending loops in
autorunAsync
.
- Undeprecated
observable(scalar)
(see 143) expr
no longer prints incorrect deprecated messages (see 143)- Requires
mobx
twice no longer fails.
Welcome to Mobservable MobX 2! First of all, there is the name change.
The new name is shorter and funnier and it has the right emphasis: MobX is about reactive programming.
Not about observability of data structures, which is just a technical necessity.
MobX now has its own mobxjs organization on GitHub. Just report an issue if you want to join.
All MobX 2.0 two compatible packages and repos have been renamed. So mobx-react
, mobx-react-devtools
etc.
For the 1.0 versions, use the old mobservable
based names.
Migrating from Mobservable should be pretty straight-forward as the public api is largely the same.
However there are some conceptual changes which justifies a Major version bump as it might alter the behavior of MobX in edge cases.
Besides that, MobX is just a large collection of minor improvements over Mobservable.
Make sure to remove your old mobservable
dependencies when installing the new mobx
dependencies!
autorun
is now allowed to have cycles. In Mobservable 1 an exception was thrown as soon as an autorun modified a variable which it was reading as well.
In MobX 2 these situations are now allowed and the autorun will trigger itself to be fired again immediately after the current execution.
This is fine as long as the autorun terminates within a reasonable amount of iterations (100).
This should avoid the need for work-arounds involving setTimeout
etc.
Note that computed values (created using observable(func)
are still not allowed to have cycles.
Creating an observable from a primitive or a reference no longer returns a getter/setter function, but a method with a .get
and .set
method.
This is less confusing, easier to debug and more efficient.
So to read or write from an observable scalar use:
const temperature = observable(27)
temperature.set(15) // previously: temperature(15)
temperature.get() // previously: temperature()
observable(scalar)
has been deprecated to make the api smaller and the syntax more uniform. In practice having observable objects, arrays and decorators seems to suffice in 99% of the cases. Deprecating this functionality means that people have simply less concepts to learn. Probably creating observable scalars will continue to work for a long time, as it is important to the internals of MobX and very convenient for testing.
MobX introduced the @computed
decorator for ES6 class properties with getter functions.
It does technically the same as @observable
for getter properties. But having a separate decorator makes it easier to communicate about the code.
@observable
is for mutable state properties, @computed
is for derived values.
@computed
can now also be parameterized. @computed({asStructure: true})
makes sure that the result of a derivation is compared structurally instead of referentially with its preview value. This makes sure that observers of the computation don't re-evaluate if new structures are returned that are structurally equal to the original ones. This is very useful when working with point, vector or color structures for example. It behaves the same as the asStructure
modifier for observable values.
@computed
properties are no longer enumerable.
The core algorithm of MobX has been largely rewritten to improve the clarity, extensibility, performance and stability of the source code.
It is now possible to define your own custom observable data sources by using the Atom
class.
It is also possible to create your own reactive functions using the Reaction
class. autorun
, autorunAsync
and @observer
have now all been implemented using the concept of Reactions.
So feel free to write your own reactive constructions!
In Mobservable 1 exceptions would be caught and sometimes re-thrown after logging them.
This was confusing and not all derivations were able to recover from these exceptions.
In MobX 2 it is no longer allowed for a computed function or autorun
to throw an exception.
- MobX is roughly 20% faster
- MobX is smaller: 75KB -> 60KB unminified, and 54KB -> 30KB minified.
- Distributable builds are no longer available in the git repository, use unpkg instead:
- Commonjs build: https://unpkg.com/mobx@^2.0.0/lib/mobx.js
- Minified commonjs build: https://unpkg.com/mobx@^2.0.0/lib/mobx.min.js
- UMD build: https://unpkg.com/mobx@^2.0.0/lib/mobx.umd.js
- To use the minified build, require / import the lib from
"mobx/lib/mobx.min.js"
(or set up an alias in your webpack configuration if applicable)
- Improved debug names of all observables. This is especially visible when using
mobx-react-devtools
orextras.trackTransitions
. - Renamed
extras.SimpleEventEmitter
toSimpleEventEmitter
- Removed already deprecated methods:
isReactive
,makeReactive
,observeUntil
,observeAsync
- Removed
extras.getDNode
- Invoking
ObservableArray.peek
is no longer registered as listener - Deprecated
untracked
. It wasn't documented and nobody seems to miss it.
- Map no longer throws when
.has
,.get
or.delete
is invoked with an invalid key (#116) - Files are now compiled without sourcemap to avoid issues when loading mobservable in a debugger when
src/
folder is not available.
- Fixed: observable arrays didn't properly apply modifiers if created using
asFlat([])
orfastArray([])
- Don't try to make frozen objects observable (by @andykog)
observableArray.reverse
no longer mutates the arry but just returns a sorted copy- Updated tests to use babel6
- observableArray.sort no longer mutates the array being sorted but returns a sorted clone instead (#90)
- removed an incorrect internal state assumption (#97)
- Add bower support
- Computed value now yields consistent results when being inspected while in transaction
- Implemented #67: Reactive graph transformations. See: http://mobxjs.github.io/mobservable/refguide/create-transformer.html
- Implemented #59,
isObservable
andobserve
now support a property name as second param to observe individual values on maps and objects.
- Fixed #77: package consumers with --noImplicitAny should be able to build
- Introduced
mobservable.fastArray(array)
, in addition tomobservable.observable(array)
. Which is much faster when adding items but doesn't support enumerability (for (var idx in ar) ..
loops). - Introduced
observableArray.peek()
, for fast access to the array values. Should be used read-only.
- Fixed 71: transactions should not influence running computations
- Fixed #65; illegal state exception when using a transaction inside a reactive function. Credits: @kmalakoff
- Fixed #61; if autorun was created during a transaction, postpone execution until the end of the transaction
- Fixed exception when autorunUntil finished immediately
toJSON
now serializes object trees with cycles as well. If you know the object tree is acyclic, pass infalse
as second parameter for a performance gain.
- Exposed
ObservableMap
type - Introduced
mobservable.untracked(block)
- Introduced
mobservable.autorunAsync(block, delay)
Removed accidental log message
Fixed inconsistency when using transaction
and @observer
, which sometimes caused stale values to be displayed.
Fix incompatibility issue with systemjs bundler (see PR 52)
map.size
is now a property instead of a functionmap()
now accepts an array as entries to construct the new map- introduced
isObservableObject
,isObservableArray
andisObservableMap
- introduced
observe
, to observe observable arrays, objects and maps, similarly to Object.observe and Array.observe
extendObservable
now supports passing in multiple object properties
- added
mobservable.map()
, which creates a new map similarly to ES6 maps, yet observable. Until properly documentation, see the MDN docs.
- Stricter argument checking for several apis.
isReactive
->isObservable
makeReactive
->observable
extendReactive
->extendObservable
observe
->autorun
observeUntil
->autorunUntil
observeAsync
->autorunAsync
reactiveComponent
->observer
(inmobservable-react
package)
- dropped the
strict
andlogLevel
settings of mobservable. View functions are by default run instrict
mode,autorun
(formerly:observe
) functions innon-strict
mode (strict indicates that it is allowed to change other observable values during the computation of a view funtion). Useextras.withStrict(boolean, block)
if you want to deviate from the default behavior. observable
(formerlymakeReactive
) no longer accepts an options object. The modifiersasReference
,asStructure
andasFlat
can be used instead.- dropped the
default
export of observable - Removed all earlier deprecated functions
mobservable
now ships with TypeScript 1.6 compliant module typings, no external typings file is required anymore.mobservable-react
supports React Native as well through the import"mobservable-react/native"
.- Improved debugger support
for (var key in observablearray)
now lists the correct keys@observable
now works correct on classes that are transpiled by either TypeScript or Babel (Not all constructions where supported in Babel earlier)- Simplified error handling, mobservable will no longer catch errors in views, which makes the stack traces easier to debug.
- Removed the initial 'welcom to mobservable' logline that was printed during start-up.
- Backported Babel support for the @observable decorator from the 1.0 branch. The decorator should now behave the same when compiled with either Typescript or Babeljs.
- Introduced
strict
mode (see issues #30, #31) - Renamed
sideEffect
toobserve
- Renamed
when
toobserveUntil
- Introduced
observeAsync
. - Fixed issue where changing the
logLevel
was not picked up. - Improved typings.
- Introduces
asStructure
(see #8) andasFlat
. - Assigning a plain object to a reactive structure no longer clones the object, instead, the original object is decorated. (Arrays are still cloned due to Javascript limitations to extend arrays).
- Reintroduced
expr(func)
as shorthand formakeReactive(func)()
, which is useful to create temporarily views inside views - Deprecated the options object that could be passed to
makeReactive
. - Deprecated the options object that could be passed to
makeReactive
:- A
thisArg
can be passed as second param. - A name (for debugging) can be passed as second or third param
- The
as
modifier is no longer needed, useasReference
(instead ofas:'reference'
) orasFlat
(instead ofrecurse:false
).
- A
- Fixed issue where @observable did not properly create a stand-alone view
- Fixed bug where views where sometimes not triggered again if the dependency tree changed to much.
- Introduced
when
, which, given a reactive predicate, observes it until it returns true. - Renamed
sideEffect -> observe
- Improved logging
- Deprecated observable array
.values()
and.clone()
- Deprecated observeUntilInvalid; use sideEffect instead
- Renamed mobservable.toJson to mobservable.toJSON
- It is no longer possible to create impure views; views that alter other reactive values.
- Update links to the new documentation.
- 2nd argument of sideEffect is now the scope, instead of an options object which hadn't any useful properties
- Deprecated: reactiveComponent, reactiveComponent from the separate package mobservable-react should be used instead
- Store the trackingstack globally, so that multiple instances of mobservable can run together
- Deprecated: @observable on functions (use getter functions instead)
- Introduced:
getDependencyTree
,getObserverTree
andtrackTransitions
- Minor performance improvements