Configuring Atoms
The Atom APIs walkthrough showed how to configure an atom instance's promise and exports. But there are a few more configuration options we haven't covered.
- How to give an atom instance a Time To Live (
ttl
). - How to use flags to mark atoms that need to be overridden.
Config Objects
The atom()
factory takes a config object as its optional 3rd parameter.
atom(key, stateOrStateFactory, config)
Some config options are simple enough, they don't need the flexibility of an Atom API. We'll look at some of these now.
ttl
When an atom instance is no longer used, you may want to destroy it - allowing its data to be garbage collected.
By default, all atoms live forever. But they can be given a ttl
to configure how long instances should stick around when they're no longer in use.
const zombieAtom = atom('zombie', null, {
// keep stale zombieAtom instances in memory for 10 minutes:
ttl: 1000 * 60 * 10,
})
// if anyone uses the instance within 10 minutes, cleanup is cancelled
ttl
is a number in milliseconds. Zedux sets a timeout for ttl
milliseconds when the atom instance goes "stale". If the atom instance is still stale when the timeout ends, Zedux destroys it.
ttl
can be set to 0
to clean up stale atom instances immediately, without a timeout. This is very common.
We'll learn more about stale atom instances in the destruction walkthrough.
flags
Any atom can be given an array of string flags.
const scrollListenerAtom = atom(
'scrollListener',
() => {
injectEffect(() => {
window.addEventListener('scroll', myScrollHandler)
return () => window.removeEventListener('scroll', myScrollHandler)
}, [])
return null
},
{
flags: ['browser-only'],
}
)
This scrollListenerAtom
uses the window
object without checking that it exists. This would throw an error if it was run, e.g., in a Node environment, so we gave it a flag to indicate this - 'browser-only'
.
The flags can be any strings; you decide what they are.
One purpose of flags is to tell Zedux to log a warning when an atom is used in an unsafe environment. To tell Zedux which flags are safe, use the flags
ecosystem config option:
return (
<EcosystemProvider flags={['browser-only']} id="root">
<Child />
</EcosystemProvider>
)
// or
const rootEcosystem = createEcosystem({
flags: ['browser-only'],
id: 'root',
})
This tells Zedux that atoms with the flag 'browser-only'
are safe in this ecosystem. That includes the scrollListenerAtom
from above.
Now what if you want to run this code in Node (e.g. for SSR). You can tell Zedux that atoms with the flag 'browser-only'
are not safe by not including it in the ecosystem's list of flags:
return (
<EcosystemProvider flags={[]} id="root">
<Child />
</EcosystemProvider>
)
// or
const rootEcosystem = createEcosystem({
flags: [],
id: 'root',
})
Now if you try to use scrollListenerAtom
in this ecosystem, Zedux will log a warning.
Open your browser console and reset the above sandbox to see Zedux log a warning. This warning tells you that you should override the scrollListener
atom in the current ecosystem.
To turn off flag warnings, just don't set ecosystem flags
.
createEcosystem({ flags: [] }) // warns on every flag
createEcosystem({}) // never warns
api.setTtl()
Atom config objects are for simple config. But sometimes you want more than just a number timeout. You can also configure an atom instance's ttl with an Atom API:
api().setTtl(10000) // set ttl to 10 seconds
api().setTtl(myPromise) // wait until myPromise resolves before destroying
api().setTtl(myObservable) // wait until myObservable emits before destroying
// function overload to defer creating the promise until the instance goes stale
api().setTtl(() => createPromise())
api().setTtl(() => createObservable()) // same thing for observables
// number also allowed, e.g. for reading dynamically from config
api().setTtl(() => 10000)
As you can imagine, this is extremely powerful. Some ideas for how to use this:
- Configure a global killswitch to clean up atom instances on-demand.
- Create a plugin that implements a FIFO queue to clean up the oldest instances when the cache reaches a certain size.
Default TTL
By default, all atom instances live forever. This default is fine for most apps. But in some cases, a default ttl of 0
or a short timeout might make more sense. Ecosystems have a config option for this, atomDefaults
. This is an object that can be given a ttl
property to set a default TTL for all atoms in the ecosystem:
atomDefaults.ttl
is a number in milliseconds.
Config Priority
Ecosystems have atomDefaults.ttl
, atoms have a ttl
config option, and Atom APIs have a setTtl
function. If you set ttl in multiple places, the "most specific" will take priority. Here's a little visual:
Ecosystem config is the most general, atom template config is more specific, and atom instance config is the most specific. Thus for TTL, the "specificity order" is (from most to least specific):
api().setTtl() -> ttl -> atomDefaults.ttl
Recap
- Set TTL using
.setTtl()
on an Atom API returned from a state factory,ttl
on an atom config object, oratomDefaults: { ttl }
in an ecosystem's config. - Set an atom's flags using the
flags
property on an atom config object. - Configure the currently-allowed flags with the
flags
property of an ecosystem's config.
Next Steps
You now know all the basics of creating and using atoms. It's time to learn some of the convenient tools that Zedux provides to make creating and using atoms even easier.