-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: observe api for observing stores with effects #55
Conversation
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. |
|
Please be patient, the PR description is not very polished yet. I'll do another pass before I publish as docs. I appreciate the feedback, so thank you for your questions.
If you are creating store in the global scope then you can just do observe((get, set) => {
console.log(`count is now ${get(countAtom}`)
}, store) Alternatively, if you are creating the store inside a component, you can do function effect(get: Getter, set: Setter) {
console.log(`count is now ${get(countAtom}`)
}
function Component() {
const store = useStore()
observe(effect, store)
...
} However, maybe the useAtomEffect recipe is better suited.
function loggingEffect((get, set) {
console.log(`count is now ${get(countAtom}`)
}
observe(loggingEffect)
observe(loggingEffect)
From above, I think the useAtomEffect recipe is better suited for calls made within a component.
This is an evolution, but each api has different applications. I'm looking for feedback on which ones are useful and which ones we can deprecate. Below is a breakdown of the different apis and their strengths and weaknesses. atomEffectThe first public api for effects in jotai. Returns an atom that runs the effect when it is mounted. const countAtom = atom(0)
const loggingAtomEffect = atomEffect((get, set) => {
console.log(`count is now ${get(countAtom}`)
})
// mount directly
store.sub(loggingAtomEffect, () => {})
// - or -
useAtomValue(loggingAtomEffect)
// - or mount by mounting an atom that references the atomEffect)
const derivedAtom = atom((get) => {
get(loggingAtomEffect)
})
useAtomValue(derivedAtom) Pros
Cons
withAtomEffectThe next api binds effects to target atoms. withAtomEffect is a functor that adds the atomEffect to a target atom, returning a derived atom that references the target. const countAtom = atom(0)
const countWithLoggingEffect = withAtomEffect(countAtom, (get, set) => {
console.log(`count is now ${get(countAtom}`)
})
// mount directly
store.sub(countWithLoggingEffect, () => {})
// - or -
useAtomValue(countWithLoggingEffect) Pros
Cons
useAtomEffectThis is a recipe for creating this hook. It is fairly simple to set up, but I wonder if it should be a public api. Unfortunately, I don't have enough information on how widespread its use it, and if it would be useful to expose as a public api. The advantage this hook has over useEffect is that it can listen and respond to changes to jotai atoms. function useCustomHook() {
useAtomEffect(
useStableCallback((get, set) => {
console.log(`count is now ${get(countAtom}`)
}, [])
)
...
} Pros
Cons
observeIs this a good idea? I am not sure. But I would love to get more feedback from the community on this PR. I do not plan to merge until I have more data.
Pros
Cons
|
275410a
to
0658276
Compare
2fee89a
to
77dfb20
Compare
422cd4d
to
77fdf0a
Compare
Summary
I'm excited to introduce a brand new helper function,
observe
, which simplifies and unifies the way you manage side-effects in Jotai. By leveragingobserve
, you can cleanly subscribe to state changes, automatically run (and clean up) your effects, and even share subscriptions between multiple calls—all with minimal boilerplate.API
Type
Example
Parameters:
get
andset
methods.observe
uses the default store.Returns:
A cleanup function (
unobserve
), which stops the effect and removes subscriptions when called.Key Features
Streamlined Subscription
Calling
observe(effect, store)
runs the effect immediately and on every relevant state change.Automatic Cleanup
The returned
unobserve
function handles cleanup for you, freeing you from managing memory leaks or forgotten subscriptions.Idempotent Subscriptions
Repeatedly calling
observe(effect, store)
reuses the same subscription. This means multiple parts of your app can rely on the same effect without incurring additional overhead.Independent Store Support
You can attach the same or different effects to any number of stores. Each store maintains its own subscriptions, keeping your state logic isolated and modular.
Multiple Effects
Attach as many independent effects to a store as you need. Each effect is tracked separately, so unsubscribing one won’t affect the others.
Complete Example
Behind the Scenes
Internally,
observe
uses aWeakMap<Store, Map<Effect, () => void>>
to avoid duplicate subscriptions.Effects run synchronously in response to state changes.
The utility handles multiple stores and concurrent effects, ensuring consistent behavior in real-world scenarios.
With
observe
, Jotai’s side-effect management becomes more powerful and ergonomic. I hope this utility simplifies your workflows and keeps your codebase clean and maintainable. Feedback is always welcome!