Replies: 3 comments 9 replies
-
|
I think it makes more sense for the returns to live in the state slot. I get two benefits from doing that:
So far I came up with this factory: export function service<Params extends any[] = [], State extends Record<string, any> | void = void>(
key: string,
value: (ecosystem: Ecosystem, ...params: Params) => State,
config?: AtomConfig<State>
): IonTemplateRecursive<{ State: State; Params: Params; Events: None; Exports: None; Promise: undefined }> {
return ion(
key,
(...args) => {
const exports = value(...args);
return injectMemo(() => api().setExports(exports ?? {}).exports, []) as State;
},
config
);
}The choice between atom and ion is an implementation detail. I just need to verify that I don't get unnecessary re-evaluations if the service code re-renders - the result is stable so it should not bubble any re evaluations up... |
Beta Was this translation helpful? Give feedback.
-
|
Hello @edongashi! Thanks for these ideas. I've been on the fence for a while on some of these things. And that fence still has a solid hold on me for one or two of them. Maybe you can finally talk me down from here.
Yes. And they would be tree-shakeable. We discussed adding these to Zedux core probably 4 years ago and I said no then. That was because I felt like people were shying away from using atom instances themselves - they were too magical/mysterious. So I wanted our devs to use So I've pivoted and am probably ready to add some helpers like this.
This is probably a candidate for the utils package (#143). I've played with a few variations of this. For example, extending class MyController extends AtomInstance {
// define methods directly on the class:
async example() {
// can access the ecosystem via `this.e` (would make this more user-friendly if we did this)
}
}
ecosystem.getNode(MyController).example() // no need for `.exports` or `.get()`, we extended AtomInstance itselfBut back to the fence: I can't decide what this API should look like (I definitely wouldn't do it like this example). Your
So this is what most signals libs would call an effect. Zedux has const viewEffect = effect("viewEffect", ({ get }) => {
const plugin = get(pluginAtom); // reactive
const someParameter = get(() => get(settingsAtom).someParameter); // reactive selector
plugin.registerView(...)
return () => plugin.detachView(...)
})
// you can then
const effectNode = ecosystem.getNode(viewEffect)
effectNode.destroy() // run cleanupI can't decide if this is useful enough for its own API. What do you think? This is probably yet another util. Although ions made it to the core package... Idk but here's a summary from this discussion (mostly for my reference):
Interestingly, all these exotic atom ideas take the ecosystem as their first parameter.
Overrides override everything. An atom override is an entirely different atom with its own state, promise, exports, ttl, etc. When calling
Yeah I understand this dilemma. My general rule is: If it can change, it's state. If it's stable, it's usually an export. I use exports for these myself. But I think I'm alone in that - looks like most of our "controller"/"builder"/"service" atoms put these functions in state. And I agree with your first argument in favor of using state - It isn't what I do, but maybe I'm wrong. I think I have to say that the prevailing wisdom seems to be to put these in state. |
Beta Was this translation helpful? Give feedback.
-
|
I am absolutely planning on renaming `useAtomInstance` and
`injectAtomInstance`, but not in v2; it had too many breaking changes
already. V3 will be a much more relaxed major version and can include
things like that.
FYI I won't have consistent internet access till Monday. Will reply to the
rest of this then
…On Sat, Apr 26, 2025, 4:01 PM Edon Gashi ***@***.***> wrote:
Also useAtomInstance and getNode doing the same thing is not immediately
obvious.
Are there any plans to rename useAtomInstance to useNode for consistency?
—
Reply to this email directly, view it on GitHub
<#245 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ADEXCU2O7GUWZWOD3E6CLML23PQ2JAVCNFSM6AAAAAB34TLS4WVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTEOJVGY4TKMQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi. I'm playing with this library (v2) and I can say that so far I am fascinated!
I tried to make my own abstractions over Jotai for some common patterns but ended up frustrated by the lack of imperative access outside of react components. In Zedux I found that everything I wanted is already possible out of the box!
The toy project I am trying to use Zedux in is a personal Obsidian plugin. What immediately struck me that I was able to refactor a good chunk of the logic to be more expressive even before touching any views. Being independent from a react tree makes life so much simpler.
One idea I really liked was the atom exports. This allows exposing higher level wrappers for mutating the state graph. I imagine sometimes I would want the exports part only, without caring about the mutable state itself. The exports being stable references is too good to pass. However, importing exports is a little verbose:
Could we have simpler hook/injectors for direcly accessing the exports? E.g.:
With tree shaking this hook would be entirely optional, no?
Another thing for these "services" is that declaring them feels hacky (pardon the idiotic example):
Could we have a factory simplified for declaring "export-only" atoms?
The downside here is that it won't be obvious to users that the returned object does not re-evaluate. Note that one can write these in userland, but it would be interesting to see if there can be an official acknowledgment of this use case.
Beside the exports, I found that I ran some initialization code inside the ecosystem via
injectEffect, say registering event handlers and some commands etc. I found that this also requires a context where I don't care about state, I simply want to run this as a background daemon. Something like a service would make sense in this case as well:Am I crazy for finding injectors extremely attractive for non-UI use cases like this?
To mount the services and make sure they live as long as the plugin I had to do a trick by instantiating a top level atom set to
ttl: -1. On plugin unload I reset the ecosystem to make sure we run the unmount callbacks on the services. Nothing wrong, just felt a bit low level and perhaps there is room to have some convenience helpers.Lastly, for DI: I think
overridesworks only for state, right? This would mean that to be able to shim a service in testing it wouldn't be so easy with exports. Would it therefore be better if the "exports" are the atom's state itself? If the service exports are in the state itself, then I suppose the responsibility would be upon you to keep them stable? In that case the "service" factory would have to take care of auto batching.I am torn between the two approaches: state service vs exports service. What are your thoughts?
Thanks and keep up the excellent work! 👍
Beta Was this translation helpful? Give feedback.
All reactions