- Fix bug where history would not completely flush all transactions, causing store handlers to fire twice.
start()
must be invoked
In a previous update, we made a change that allowed instances of
microcosm to work without invoking start()
. This update reverts that
decision. Without intentionaly invoking start, transactional state
becomes hard to predict. This is potentially a breaking change; for
those upgrading, verify that you are calling start
before using a
microcosm.
We improved the validation of stores to help improve debugging of
bad inputs to Microcosm::addStore
.
- Small change to dispatch process so that accessing state only happens when necessary. This should provide a small performance boost.
- Addressed an IE9 bug where stores passed without key paths did not install properly.
Microcosm::addStore
mounts stores to a given key path, like:app.addStore([ 'path', 'to', 'key' ], Store)
. Additionally, adding a store without a keypath will mount it to the entire application state. This is to improve the useability of Stores that must operate all state (such as make decisions about game state).- Renamed
setFocus
tocheckout
in internal Tree structure. This is a breaking change. The goal is to move towards a more intuitive API. - Tweaked build process to prevent babel compilation errors when developing for React Native development.
The enhancement Microcosm::addStore
is not a breaking change, all
old use cases will continue to work.
Those experimenting with app.history
will need to rename calls to
setFocus
to checkout
.
This is a big update, however there should be no breaking changes (assuming you are not referencing Microcosm internals).
- Microcosm now stores transactions created by actions as a tree. The long term plan for this change is to support undo trees.
- Stores determine initial state when they are added to a
Microcosm. This allows for Microcosms to be created without needing
to
start()
. - Added history API. This is an unstable API. However, for those curious, check out the undo-tree example.
- Added some additional validations to ensure proper use of Microcosm.
- Actions that are generators now receive the last payload as the
returned value from
yield
. This should help to improve sequential, daisy chained, calls. - Adjusted build tooling to expose Microcosm modules at
microcosm/*
instead ofmicrocosm/src/*
- Stores and Plugins can now be functions. When this is the case, they
will act as the
register
function in each instance.
- Adjustments to improve v8 performance. All Microcosm operations should occur without deoptimization penalties.
For those referencing Microcosm internals, we have moved their hosted
directory from src
to the folder root. This means the following
changes are necessary:
Instead of:
require('microcosm/src/lifecycle')
Change this to:
require('microcosm/lifecycle')
- Generators used for Microcosm actions can now yield other generators. In these instances, child generators operate to completion before the next iteration of the parent generator.
- Upgrade Diode to 6.1.0.
listen
now supports a second argument that defines the scope of the callback. - Respect scope of bound functions when executing callbacks via
app.push
- Fixed bug where
eventually
would try to execute a non-function value
- Improved the error messages for
addStore
- Changed order of execution in
tag
to prevent unnecessary work
- Each store will receive the reduced state from all prior stores. This means that stores can respond to the result from prior operations. This should not affect any stores that do not access the third argument of store callbacks (all application state).
- Rewrites and improvements to
dispatch
andsend
methods to achieve higher v8 optimization.
- If a store returns undefined, no state change will occur. This is
potentially a breaking change. If you have stores that return
undefined
, consider changing your logic to support returningnull
- The third argument of store callbacks now contains all application state. The intention behind this addition is to allow for stores that must make decisions based upon input from multiple sources.
- The
deserialize
lifecycle method is now provided the entire raw state as the action parameters. This means that it is now available as the second argument in store callbacks. - Similarly, the
serialize
lifecycle method is now provided the entire app state in the action. This means that it is now available as the second argument in store callbacks.
- Tweaks to lazy callback executed after
app.push
for better optimization - Tweaks to
flatten
for better optimization - Renamed
async
utility tocoroutine
- Reworked transactions to expose future lifecycle methods
- Retain 100% test coverage
There are no breaking changes for this release.
- Upgrade dependencies
- Use fixed versions for dependencies
- Fixed bug where lifecycle methods used as registered actions did not properly stringify.
- Exposed lifecycle actions under
microcosm/lifecycle
. See the upgrading section for more notes.
getInitialState
,serialize
, anddeserialize
are now triggered by actions. We call them lifecycle actions. Their associated counterparts arewillStart
,willSerialize
, andwillDeserialize
. There is no obligation to use these lifecycle actions, the store methods should work all the same.
This version adds lifecycle actions. This does not make any breaking change to the Microcosm API, however it provides us better internal consistency.
These lifecycle actions are still undergoing development (names may change, etc, but we'll keep you posted). However if you would like to give them a spin, consider the following code example:
import { willStart } from 'microcosm/lifecycle'
import { addPlanet } from 'actions/planets'
const Planets = {
reset() {
return []
},
add(records, item) {
return records.concat(item)
},
register() {
return {
[willStart] : Planets.reset,
[addPlanet] : Planets.add
}
}
}
- Store registration methods can return non-function values. When this is the case, it will use this value as the new state.
- Plugins will now validate that their
register
property is a function. If this property is not present, it will skip this validation and continue to the next plugin.
- Internalized
is-generator
package to reduce dependencies and cut some dead code. - Refactored the install process to prevent needless extension and simplify the installation queue.
All changes are purely internal polish. There should be no additional required action. The build is about 100 bytes smaller, but who's counting? :)
- Updates to the way transactions are created and rolled forward to improve efficiency and support dev tool development
- Microcosm now uses transactions to process state. When an action is pushed,
an associated transaction will be created. Transactions are processed in the
order in which
app.push
is called. - Added a mechanism for optimistic updates using generators in actions.
app.push
accepts a callback as the third argument which will be invoked when an action is completely resolved (More in breaking changes)
- Removed Foliage. Microcosm no longer extends from Foliage and its API is no longer available.
- Instead of
app.get
orapp.toObject()
to retrieve state, useapp.state
. - The signature for
app.push
is nowapp.push(action, [...arguments], callback)
. - The signature for
app.prepare
is nowapp.prepare(action, [...arguments])
.
For those using the Foliage API, consider using Foliage within Stores themselves.
app.push
should continue to work as expected when only one parameter is pushed to an action, however those pushing multiple parameters should make the following change:
// Before:
app.push(action, 'one', 'two',' 'three')
// After:
app.push(action, ['one' ,'two', 'three'])
Additionally, the third argument of app.push
is now an error-first callback.
When an action resolves or fails, it will execute this callback:
app.push(action, params, function(error, body) {
if (error) {
handleError(error)
}
})
All instances of app.get('key')
should be replaced with app.state.key
, sort of like
if it were a React Component
- Microcosm will emit events synchronously.
In the past, Microcosm would use requestAnimationFrame to batch together changes. However this can cause unexpected consequences when sequentially performing otherwise synchronous operations. For those who wish to preserve this behavior, consider using debounce to "choke" high frequency changes.
- Upgrade Foliage to
0.24.0
. - Moved
Store.prototype.send
toStore.send
. This has always been an internal API, however those using this method for testing will need to update. This change is motivated by a desire to reduce as much surface area from Store instances as possible. - We now use
babel-plugin-object-assign
for extension - Microcosm is compiled in normal babel mode (not loose)
- Store responses to actions will always be called within the scope of the store.
- Addressed classical inheritance issue not caught from
loose
babel compilation
For those using Store.prototype.send
, the following change is
necessary:
// Before
store.send(state, action, payload)
// After
Store.send(store, action, state, payload)
- Stores no longer return
this
fromregister()
by default. This is a potentially breaking change, however should not pose a problem to projects using idiomatic Store registration. - Scope of store reducers when dispatching will always be the Store
- Added plugin class to manage defaults
tag
now includes the name of the function intoString()
- Unique ids for plugins and actions are internally generated with counters
- Stores now contain the logic for how it should receive an action.
logic is contained under
send
. - Stores now contain the logic to determine what method should resolve
an action sent to it. This is defined in
register
Microcosm::deserialize
will now only operate on the keys provided by the seed object. This means that data passed intoreplace
will only blow way keys provided in the data object.- The signaling logic for dispatching actions will throw an error if the action provided is not a function
- Internalized tag, it will now lazy evaluate as actions are fired
- Upgraded Foliage, Microcosm now contains
subscribe
,unsubscribe
, andpublish
aliases forlisten
,ignore
, andpublish
- Remove all uses of the
tag
module.
Before this release, stores would listen to actions using the stringified value of their functions:
var MyStore = {
[Action.add](state, params){}
}
This was terse, however required actions to be tagged with a special helper method. It also required any module that needed access to a Store's method to also know what actions it implemented.
To address these concerns, Stores now communicate with a Microcosm
using the register
method:
var MyStore = {
register() {
return {
[Action.add]: this.add
}
},
add(state, params){}
}
Under the hood, Microcosm tags functions automatically.
- Bumped Foliage to a newer version
Microcosm::start
will return itself
- Replaced all uses of ES6 modules with CommonJS. This was causing issues in non-ES6 module projects.
- Microcosm publishes as separate modules now. Ideally, this will make internal pieces easier to reuse and help with debugging.
- Internally, Microcosm now uses Foliage for state management.
pull
is nowget
, as it is inherited from Foliage- Microcosm is actually an extension of Foliage, so it now includes all Foliage methods.
- Microcosm no longer uses toString() to get the key for Stores. This was decided upon so that it is easier to reason about what a Store is responsible for when hooking it into a Microcosm.
- Externalize some methods to fix extension
- Microcosm's event system has been replaced with Diode. The APIs are the same. This should not lead to any breaking changes.
Microcosm::pull
can now accept an array of keys for the first argument. This will traverse the nested keys of state to calculate value.
6.0.0 is the second effort to reduce the surface area of the Microcosm API.
- Removed
Upstream
andDownstream
mixins. They used the undocumented context API and introduced some complexity in testing Microcosm::send
is nowMicrocosm::push
Microcosm::push
is nowMicrocosm::replace
Microcosm::dispatch
andMicrocosm::commit
are now private. These are important methods that should not be overridden
Microcosm::pull
accepts a callback that allows you to modify the result. This should help to make data queries more terse.- Removed
Microcosm::clone
, the functionality is not gone, but it has been internalized to mitigate the cost of future changes - Removed mixins from main payload to improve size
- Fix build process mistake :-/
- Removed fallback from
Microcosm::pull
which returns all state - Added an
Upstream
andDownstream
mixin, however it is experimental. More details will come as the feature develops. Microcosm::send
will throw an error if given an undefined action parameter
Version 5 represents an attempt to address some growth pains from rapidly adding new features to Microcosm. Names have been changed to improve consistency and internal APIs have been refactored. The overall surface area of the app has been reduced and more opinions have been made.
- Renamed
Microcosm::seed
toMicrocosm::push
- Renamed
Microcosm::get
toMicrocosm::pull
- Removed
Microcosm::has
- Removed
Microcosm::getInitialState
. theStore
API still provides this function, however it is the expectation of the system that value of state is a primitive object. This is so that Microcosm always knows how to smartly clone its state, regardless of if another data library is used for its values. - Removed
Microcosm::swap
, this was an internal API that is no longer required - Renamed
Microcosm::reset
toMicrocosm::commit
- Removed
Microcosm::shouldUpdate
. If no stores respond to an action, a change event will not fire anyway. Placing this concern in the view layer keeps React'sshouldComponentUpdate
as the single responsibility for this task. - Added
Microcosm::toObject
- Internal function
mapBy
has been renamed toremap
. It now operates primarily upon objects. Microcosm::pump
is nowMicrocosm::emit
, this is to better match existing event emitter libraries (including the one in Node's standard library)
As an additional illustration, the Microcosm API has been logistically
sorted within ./cheatsheet.md
- Added concept of plugins. Plugins provide a way to layer on additional functionality. This has specifically been added so that environment specific behavior may be added to an app.
- Added
Microcosm::start
. Callingstart()
will bootstrap initial state, run all plugins, then execute a callback.
mapBy
internal function now accepts an initial value- Changed
Microcosm::dispatch
copy strategy. Instead of merging a change set, it now directly modifies a clone of the previous state. - Added
Microcosm::clone
. This method defines how state is copied before dispatching an action.
- Changed default shouldUpdate algorithm
Microcosm::getInitialState()
now accepts anoptions
argument. This argument is passed down from the constructor.
- Changed data update pattern to more closely match
Om. This means
that
Microcosm::merge
has been replaced withMicrocosm::swap
. Additionally,Microcosm::reset
has been added to completely obliterate old state. Microcosm::addStore
now only accepts one store at a time. It was not being utilized, gives poorer error handling, and makes let less clear the order in which Stores will process data.- The internal class
Heartbeat
was replaced withpulse
. Pulse is a function that can act as a factory or decorator. When given an argument, it extends an object with emitter functionality, otherwise it returns a new object that implements the same API. This eliminates the possibility that the private_callbacks
member ofHeartbeat
was overridden. It also reduces the use of classical inheritance, which yields some minor file size benefits by polyfilling less of theclass
API.
- Fix issue where empty arguments would break deserialize
- Replace default
Microcosm::send
currying with partial application usingMicrocosm::prepare
- Throw an error if a store is added that does not have a unique identifier
Microcosm::set
has been replaced withMicrocosm::merge
, so farset
has only been used internally toMicrocosm
andmerge
dries a couple of things up
Currying has been removed Microcosm::send
. This was overly clever
and somewhat malicious. If an action has default arguments, JavaScript
has no way (to my knowledge) of communicating it. One (me) could get into a
situation where it is unclear why an action has not fired properly
(insufficient arguments when expecting fallback defaults).
In a language without static typing, this can be particularly hard to debug.
In most cases, partial application is sufficient. In light of this,
actions can be "buffered up" up with Microcosm::prepare
:
// Old
let curried = app.send(Action)
// New
let partial = app.prepare(Action)
Microcosm::prepare
is basically just fn.bind()
under the
hood. Actions should not use context whatsoever, so this should be a
reasonable caveat.
Store.deserialize
returns the result ofgetInitialState
if no state is given- Added
Microcosm.swap
to perform diffing and emission on change Microcosm.seed
will now trigger a change eventHeartbeat.js
now invokes callbacks withcallback.call(this)
- Microcosms will
set
the result ofgetInitialState
when adding a store - Microcosms will execute
deserialize
on stores when runningseed
- Adding a store will now fold its properties on top of a default set
of options. See
./src/Store.js
for details.
- Fix bug introduced with Tag by exposing ES6 module
- All stores can implement a
serialize
method which allows them to shape how app state is serialized to JSON.
- Better seeding. Added
Microcosm::seed
which accepts an object. For each known key, Microcosm will the associated store'sgetInitialState
function and set the returned value. - Exposed
Microcosm::getInitialState
to configure the starting value of the instance. This is useful for those wishing to use theimmutable
npm package by Facebook. - Microcosm will not emit changes on dispatch unless the new state
fails a shallow equality check. This can be configured with
Microcosm::shouldUpdate
Microcosm::send
is now curried.
This version adds many breaking changes to better support other libraries such as Colonel Kurtz and Ars Arsenal.
In summary, these changes are an effort to alleviate the cumbersome nature of managing unique instances of Actions and Stores for each Microcosm instance. 1.0.0 moves away from this, instead relying on pure functions which an individual instance uses to operate upon a global state object.
- Actions must now be tagged with
microcosm/tag
. For the time being, this is to provide a unique identifier to each Action. It would be nice in future versions to figure out a way to utilizeWeakMap
. - Stores are plain objects, no longer inheriting from
Store
base class. - Stores must implement a
toString
method which returns a unique id. - State for a store must now be accessed with:
microcosm.get(Store)
- Microcosms no longer require
addActions
, actions are fired withmicrocosm.send(Action, params)
- Removed
Microscope
container component. Just uselisten
- Remove
get all()
fromStore
. This is to reduce namespace collisions. Stores should define their own getters.
- Added a
pump
method toMicrocosm
instances. This exposes the heartbeat used to propagate change.