The current plan is to completely rework how plugins work in Zedux v2.
The Current Model
Plugins in Zedux v1 are purposefully verbose. You instantiate a class, set up your initial "mods", manually subscribe to the ecosystem's mod store with a verbose effects subscriber, and add ugly checks for mod action types.
Plugins previously might have maintained internal state, turned on multiple mods, and/or returned a cleanup function. This can all be done in the new model. Conventionally, a "plugin" will now be a simple function that accepts the ecosystem and calls .on. It can instantiate and use atoms in the ecosystem (or its own internal ecosystem), store internal state, and clean it all up on destroy or reset:
const logAtom = atom('log', [])
const loggingPlugin = (ecosystem: Ecosystem) => {
// other internal state can be tracked here:
let internalState = []
const cleanupChange = myEcosystem.on('change', reason => {
ecosystem.getNode(logAtom).set(state => [...state, reason])
internalState.push(reason)
})
// plugins can listen to `destroy` and/or `reset` to destroy internal state
const cleanupDestroy = ecosystem.on('destroy', () => {
cleanupChange()
cleanupDestroy()
ecosystem.getNode(logAtom).destroy(true)
internalState = []
})
}
loggingPlugin(myEcosystem)
Note that the destroy handler in this example is unnecessary - all four of its cleanup operations would happen automatically if the ecosystem is garbage collected. This is just for demonstration.
Communicating with Plugins
Just like with the new GraphNode#on model described in #115, Ecosystem#on will support custom events. This will allow you to send anything you want to plugins via Ecosystem#send.
We'll also document how to create a typed useEcosystem hook and injectAtomGetters injector (or injectEcosystem if we switch to that) so all interactions with your ecosystem have event types intact.
The Plugin Events
The concept of "mods" shall die, replaced with "plugin events". Some of the mods will have event equivalents. Some will be removed completely:
ecosystemWiped - replaced with the reset event
edgeCreated - replaced with the edge event
edgeRemoved - replaced with the edge event
evaluationFinished - replaced with the runEnd event
instanceReused - removed. This was never good at what it does. Build or lint plugins should handle atom key uniqueness checking (or even generating).
stateChanged - replaced with the change event
statusChanged - replaced with the cycle event
The full list of built-in events is:
change - called on node state change
cycle - called on node lifecycleStatus change (when it becomes active, stale, or destroyed)
destroy - called on ecosystem destroy (cycle is used for node destruction)
edge - called when an edge is added, removed, or its flags are changed in the graph
error - called when a node evaluation errors or atom promise rejects
reset - called when the ecosystem is reset
runEnd - called when a node finishes evaluating
runStart - called when a node starts evaluating
Zedux will no longer track evaluation time internally (previously a feature of the evaluationFinished mod). Instead, use a combination of runStart and runEnd to track it yourself. This gives more control and allows us to remove one of Zedux's only two non-deterministic APIs (ecosystem._idGenerator.now) which will make it easier for users to test Zedux code (especially via snapshot testing).
Full Change List
These APIs will be removed:
Ecosystem#registerPlugin
Ecosystem#unregisterPlugin
Ecosystem#modBus
Ecosystem#_mods
The ZeduxPlugin class
The pluginActions object (accessed via the static ZeduxPlugin.actions property)
These APIs will be added:
Ecosystem#on
Ecosystem#send
A single-letter public property (name TBD) on Ecosystem that functions similarly to the _mods property, except it won't be initialized with all possible keys. Mods will instead add event keys to this object as needed and delete keys when the event has no more listeners. This object maps event keys to a Set of event listeners.
Additional Considerations
Instead of the new concept of a plugin being a simple function, we might want to export a new plugin factory for creating Plugins with event maps intact and add a plugins ecosystem config option to auto-type the ecosystem's event map. Besides automatic type inference, this would allow plugins to return a cleanup function again, removing the need for the new destroy event.
Theoretical futuristic code:
import { createEcosystem, type NodeFilter, plugin } from '@zedux/react'
const Placeholder = {} as any
const loggingPlugin = plugin(
'logging',
ecosystem => {
ecosystem.on('change', reason => {
console.log('node state updated', reason)
})
ecosystem.on('logAll', filterOptions => {
console.log('all atoms:', ecosystem.findAll(filterOptions))
})
// can return cleanup function here (unnecessary in this case)
},
{
logAll: Placeholder as NodeFilter,
}
)
const ecosystem = createEcosystem('root', {
plugins: [loggingPlugin],
})
ecosystem.send('logAll', { excludeFlags: ['disable-logging'] })
The current plan is to completely rework how plugins work in Zedux v2.
The Current Model
Plugins in Zedux v1 are purposefully verbose. You instantiate a class, set up your initial "mods", manually subscribe to the ecosystem's mod store with a verbose effects subscriber, and add ugly checks for mod action types.
Fast forward a couple years. We now know what plugins actually need. We can make this a lot better.
The New Model
Plugins will simply be calls to
ecosystem.on
.Wow such clean 🤩.
Plugins previously might have maintained internal state, turned on multiple mods, and/or returned a cleanup function. This can all be done in the new model. Conventionally, a "plugin" will now be a simple function that accepts the ecosystem and calls
.on
. It can instantiate and use atoms in the ecosystem (or its own internal ecosystem), store internal state, and clean it all up ondestroy
orreset
:Note that the
destroy
handler in this example is unnecessary - all four of its cleanup operations would happen automatically if the ecosystem is garbage collected. This is just for demonstration.Communicating with Plugins
Just like with the new
GraphNode#on
model described in #115,Ecosystem#on
will support custom events. This will allow you to send anything you want to plugins viaEcosystem#send
.Ecosystems will get a new generic for event maps:
We'll also document how to create a typed
useEcosystem
hook andinjectAtomGetters
injector (orinjectEcosystem
if we switch to that) so all interactions with your ecosystem have event types intact.The Plugin Events
The concept of "mods" shall die, replaced with "plugin events". Some of the mods will have event equivalents. Some will be removed completely:
ecosystemWiped
- replaced with thereset
eventedgeCreated
- replaced with theedge
eventedgeRemoved
- replaced with theedge
eventevaluationFinished
- replaced with therunEnd
eventinstanceReused
- removed. This was never good at what it does. Build or lint plugins should handle atom key uniqueness checking (or even generating).stateChanged
- replaced with thechange
eventstatusChanged
- replaced with thecycle
eventThe full list of built-in events is:
change
- called on node state changecycle
- called on nodel
ifecycleStatus change (when it becomes active, stale, or destroyed)destroy
- called on ecosystem destroy (cycle
is used for node destruction)edge
- called when an edge is added, removed, or its flags are changed in the grapherror
- called when a node evaluation errors or atom promise rejectsreset
- called when the ecosystem is resetrunEnd
- called when a node finishes evaluatingrunStart
- called when a node starts evaluatingZedux will no longer track evaluation time internally (previously a feature of the
evaluationFinished
mod). Instead, use a combination ofrunStart
andrunEnd
to track it yourself. This gives more control and allows us to remove one of Zedux's only two non-deterministic APIs (ecosystem._idGenerator.now
) which will make it easier for users to test Zedux code (especially via snapshot testing).Full Change List
These APIs will be removed:
Ecosystem#registerPlugin
Ecosystem#unregisterPlugin
Ecosystem#modBus
Ecosystem#_mods
ZeduxPlugin
classpluginActions
object (accessed via the staticZeduxPlugin.actions
property)These APIs will be added:
Ecosystem#on
Ecosystem#send
Ecosystem
that functions similarly to the_mods
property, except it won't be initialized with all possible keys. Mods will instead add event keys to this object as needed and delete keys when the event has no more listeners. This object maps event keys to aSet
of event listeners.Additional Considerations
Instead of the new concept of a plugin being a simple function, we might want to export a new
plugin
factory for creating Plugins with event maps intact and add aplugins
ecosystem config option to auto-type the ecosystem's event map. Besides automatic type inference, this would allow plugins to return a cleanup function again, removing the need for the newdestroy
event.Theoretical futuristic code: