(jotai/valtio) Improving DX around Promise use
See original GitHub issueThis is a continuation of https://github.com/pmndrs/jotai/pull/625
As mentioned, the current use case feels a little bit awkward. In the original issue I mentioned three options, and @dai-shi added a fourth:
- We could introduce something under the hood of
atomWithProxy
that would introduce this as “magic” (turn values into Promise.resolve behind the scenes).
Pros: Use doesn’t have to do this weird Promise.resolve() thing Cons: This behavior is different from valtio itself. Why is it different? We need a proper mental model explanation both in-code and in docs.
- We could override this on the underlying typing level (atomWithProxy somehow overriding valtio’s types to “force” it on valtio). Same pros & cons as above. But I actually find this one odd, because if you override types to do something valtio doesn’t support - why doesn’t valtio support it? It’s a design discussion here.
One reason for 1&2 to behave different in atomWithProxy vs. just vanilla valtio is of course that the user is rarely exposed to the Promise (which suspends React render) but always sees the value on the snapshot. But I wonder if it’s that good to introduce magic that significantly changes the mental model of the underlying library. Thoughts?
- We could canonize this behavior (Once a promise always a promise) and explain it well in both comment and docs.
- Only accept proxy type with Promise<T> | T.
atomWithProxy(proxy({ n: Promise.resolve(1) })) // this will cause TS error
atomWithProxy(proxy({ n: Promise.resolve(1) as Promise<number> | number })) // this will pass
I think this is precise typing for the current behavior. Two issues: (a) not sure if we can really implement this, (b) we should document this well because the TS error message wouldn’t be so friendly.
The main problem I see with option 4 is that it isn’t exactly precise typing per-se from valtio
point of view. It is from atomWithProxy
but only because it uses lots of snapshot
. Implementing this as magic behind the scenes with TS makes option (4) and (2) the same IMHO.
Also, in my particular use case even though I use atomWithProxy
, I do lots (most of) changes to the state outside of React. I feel like it’s important to mention because in your anticipated use, vanilla wasn’t as important.
For now the experience feels awkward, but it works. I’m honestly very happy with how valtio plays together with jotai in my usecase (authentication connecting many non-react pieces with react).
I almost feel like it might be more of a Documentation issue right now (document the current compromises and workaround very well), and then we can watch & see how use-cases evolve and how other users other than myself use this library in the wild.
This is a bit of a rant, but the TLDR; is that my vote is for (4) or (2, being kind of a TS override to make 4 automatic) with lots of documentation.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:11 (10 by maintainers)
Thanks for the summary. You captured it very well. To me, I didn’t know if proxy with promises works with
atomWithProxy
, so this is already good enough.Option 1 sounds possible to me, but it does extra work if our type is
Promise<T> | T
, because JS doesn’t know about the type.I’m not sure if I understand option 2. Maybe “TS override” isn’t clear for me. That doesn’t sound something I would want.
Option 4 + docs is my preference, but it’s not very strong preference.
I would like to make this to work in TypeScript.
Another side note: valtio was originally developed only for react. (well, tbh, it’s developed to make use of proxy-compare, because react-tracked hadn’t got much attention.) I now understand the value of valtio/vanilla, as I developed this
atomWithProxy
and recently developed valtio-yjs. Both are not react specific.Yet another side note: I start to think about jotai/vanilla, as now I experiment jotai-jsx which doesn’t depend on react…
Yeah, AFAIR ,we fixed a bug and what’s remaining is documentation about the usage and the limitation.