Skip to main content

injectAtomState

import { injectAtomState } from '@zedux/react'

An injector that accepts an atom template and its params and registers a dynamic graph dependency on the resolved atom instance. Returns a tuple of the instance's store's current value and an export-infused state setter function.

This injector's return type is very similar to React's useState() hook.

const [val, setVal] = useState()
// compare:
const [state, setState] = injectAtomState(myAtom, [...params])

The atom instance that uses this injector will reevaluate whenever the resolved atom instance's state changes.

The returned state setter function is "export-infused", meaning it has all the atom instance's exports attached to it. This facilitates using exports to update the atom's state, rather than updating the state directly.

Example

Live Sandbox
12345678910111213141516171819202122232425262728293031
const secondsAtom = atom('seconds', () => {
const store = injectStore(0)

injectEffect(() => {
const intervalId = setInterval(() => store.setState(val => val + 1), 1000)

return () => clearInterval(intervalId)
}, [])

return store
})

const wrapperAtom = atom('wrapper', () => {
const [seconds, setSeconds] = injectAtomState(secondsAtom)

return api(seconds).setExports({
increment: () => setSeconds(val => val + 1),
})
})

function Seconds() {
const state = useAtomValue(wrapperAtom)
const { increment } = useAtomInstance(wrapperAtom).exports

return (
<>
<div>Seconds: {state}</div>
<button onClick={increment}>Increment</button>
</>
)
}

Destructuring the exports from the state setter:

import { api, atom, injectAtomState } from '@zedux/react'

const passwordAtom = atom('password', () => {
const store = injectStore('')

return api(store).setExports({
clear: () => store.setState(''),
setPassword: (newPassword: string) => store.setState(newPassword),
})
})

const formAtom = atom('form', () => {
// destructure the exports directly off the state setter:
const [password, { clear, setPassword }] = injectAtomState(passwordAtom)

// this is a convenient alternative to the following:
const passwordInstance = injectAtomInstance(passwordAtom)
const password = injectAtomValue(passwordInstance)
const { clear, setPassword } = passwordInstance.exports
})

Miscellaneous:

const [state, setState] = injectAtomState(myAtom)
const withParams = injectAtomState(myAtom, ['param 1', 'param 2'])
const fromInstance = injectAtomState(myAtomInstance)
const [, setterOnly] = injectAtomState(myAtom)
const [, { exports, only }] = injectAtomState(myAtom)

Signature

injectAtomState = (atom, params?) => [state, setState]
atom

Required. An atom template or atom instance.

If an atom template is passed, you must also pass any required params of the atom.

If an atom instance is passed, the params are ignored. In this case, the returned state value will be the current value of this atom instance. The returned state setter will have all the exports of the given instance attached.

In all cases, injectAtomState() adds a dynamic dependency on the resolved instance.

params

Required if the passed atom template takes required params. Optional if not.

Don't pass this or pass an empty array if the atom does not take params or if passing an atom instance.

Returns

A [state, setState] tuple:

  • state - The current value of the resolved atom instance's store.
  • setState - An export-infused state setter function.

Uses the passed atom template + params combo to find an existing atom instance. If no instance is found, creates one using the template and params.

The returned state setter is a direct wrapper around instance.setState(). It accepts either the full new state or a function that receives the current state and returns the new state.

Note that this is not a deep setter like store.setStateDeep().

The returned state setter is "export-infused" meaning it has all the exports of the resolved atom instance spread onto it (yes, onto the function itself).

const [state, setState] = injectAtomState(myAtom)
const { export1, export2 } = setState
// or simply:
const [state, { export1, export2 }] = injectAtomState(myAtom)

Yes, this means that if your exports have name collisions with the function's own properties, the exports will overwrite stuff on the function. TS users will see this, as the types correctly indicate this.

If this feels hacky to you, just remember that this injector is provided for convenience. You don't have to use it if it inconveniences you. There are other ways to accomplish everything this injector does. For example:

const instance = injectAtomInstance(myAtom)
const state = injectAtomValue(instance)
const { setState } = instance
const { export1, export2 } = instance.exports

See Also