Skip to content
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

Return context or Consumer from createContext #402

Closed
barbogast opened this issue May 26, 2021 · 16 comments
Closed

Return context or Consumer from createContext #402

barbogast opened this issue May 26, 2021 · 16 comments

Comments

@barbogast
Copy link
Collaborator

First of all, thanks to all of you, working with zustand is pretty neat.

Would it be possible for createContext() to return the context or context.Consumer? This would enable bridging the context between renderers.

In my case I need to bridge the context of zustand into react-konva. See konvajs/react-konva#188 (comment) and facebook/react#13336 for more context.

@dai-shi
Copy link
Member

dai-shi commented May 26, 2021

zustand is originally designed to work with multiple renderers.
curious, why do you need createContext in your use case?

We could add bridge support, but it's a bit ironic, and practically I'm not sure if we can document it well. This sounds to me, a very rare case. So, one practical option would be to create context at your end instead of using zustand/context.

@barbogast
Copy link
Collaborator Author

barbogast commented May 26, 2021

I'm using the persist middleware and need the store to be hydrated before the application is fully rendered. I'm using this approach: #346 (comment) (... and yeah, it would be nice if I didn't have to :-) )

We could add bridge support, but it's a bit ironic, and practically I'm not sure if we can document it well. This sounds to me, a very rare case. So, one practical option would be to create context at your end instead of using zustand/context.

I don't know how rare the case is but it seems to be a thing, there are a couple of libraries out there which have separate renderers. And since zustand does support being used with a context it makes sense to support bridging it, doesn't it?

Here is a PR from recoil which seems to implement something similar: facebookexperimental/Recoil#140

I could solve my use case locally by just add context to the return statement here: https://github.com/pmndrs/zustand/blob/master/src/context.ts#L69. I could create my own fork of zustand, but it would be great if it would be in the official repository. I could also create a PR, no problem.

In terms of documentation... I guess it depends on the frequency of the issue. Maybe it doesn't need to be mentioned explicitly in the documentation.

@dai-shi
Copy link
Member

dai-shi commented May 26, 2021

need the store to be hydrated before the application is fully rendered.

There's some work in progress, which may solve your issue.

I could solve my use case locally by just add context to the return statement

Yes, if this is only for you, it's fine. But for the library, this would break the whole point of zustand/context which is to hide the bare context to avoid misusing (the purpose is to limit the usage.)
So, if we were to really support the bridging use case, we'd need to expose something like Bridge component and useBridge hook. (which I have done in another project.)

Only the case this would be required is server side rendering + multiple renderers. And, I think it's too rare.

Let's wait for the wip fix (no PR yet), and see if it solves your use case.

@barbogast
Copy link
Collaborator Author

the whole point of zustand/context which is to hide the bare context to avoid misusing

I wasn't aware, makes sense to not expose it, then. Will wait for the fix (I guess you're referring to #366 (comment)). Thanks anyway :-)

@dai-shi
Copy link
Member

dai-shi commented May 26, 2021

#403 is filed.

@omasback
Copy link
Contributor

omasback commented Jun 7, 2021

I just ran into this same issue and I don't think this is an unusual use case. I may have multiple konva or r3f canvases that each need their own instance of a zustand store, precluding singleton usage and necessitating context usage. As of now I'm stuck with passing down the original hook via normal react context and can't use the new zustand context at all.

@barbogast
Copy link
Collaborator Author

As a side note, I'm using several different stores within the same app without using context. Using several instances of the same store I'd expect to work in the same way. This did successfully solve the issue for me, since context bridging was no longer required.

Still, your point stands @omasback

@dai-shi
Copy link
Member

dai-shi commented Jun 7, 2021

I'm not sure if I understand the point.
Could you explain the use case that requires context with r3f canvases? There might be, but I don't think it's very usual...

@omasback
Copy link
Contributor

omasback commented Jun 7, 2021

Okay I don't know much about r3f but I assume it's similar to konva where it uses a different renderer for the canvas so if you want to consume context within the canvas (that is initially provided outside the canvas) you need to bridge the context like so: https://github.com/konvajs/react-konva#usage-with-react-context

edit: Seems to be an issue in r3f as well. https://standard.ai/blog/introducing-standard-view-and-react-three-fiber-context-bridge/

@dai-shi
Copy link
Member

dai-shi commented Jun 7, 2021

@omasback I'm aware of context limitation and bridge thing. And that's why zustand is originally developed, to fix multi renderer issue. It just works with useStore (module state).

zustand/context is specifically provided for server side rendering with single renderer. Do you think this is a doc issue?

@omasback
Copy link
Contributor

omasback commented Jun 7, 2021

Huh I didnt realize zustand/context was primarily built with SSR in mind. I don't do SSR at all in my current project. I use context to enable multiple instances of the same store type (as in a list of components that each have their own store).

Also, I like to create/destroy stores as part of component lifecycle. So even if I have only one instance of a store at a time, for example to control a certain page of my app, I like to create that store when the page mounts, pass it through context, and then destroy the store when the page unmounts.

So I guess I must admit that I've been using zustand in the "off label" way this entire time!

@dai-shi
Copy link
Member

dai-shi commented Jun 8, 2021

I like to create/destroy stores as part of component lifecycle.

Okay, now I get it. This is a valid pattern in zustand, but I state it as "for experts" because creating a hook in component lifecycle is not considered a good pattern in general (as it can break rules of hooks easily.)

zustand/context is designed to accept only initialStore that can't be changed afterward, so that users can't violate rules of hooks. So, it doesn't fit with your use case. Instead, create context on your end. I guess you already does it with putting your store hook in useRef or useState.

@ivanyurchenk0
Copy link

I also added a small demo to show a current behavior that is looking very strange in terms of context usage https://codesandbox.io/s/ivanyur4enk0-zustand-createcontext-issue-ym9fp if compare it even with react context. I did not find any details about that behavior on https://github.com/pmndrs/zustand#react-context page that assume single provider and explanation that all contexts will work with the same state. Is it possible to extend zustand createContext to support independent contexts that will work without using React provider that required default value and all other boilerplate code from upper sample?

@dai-shi
Copy link
Member

dai-shi commented Jun 15, 2021

@dai-shi
Copy link
Member

dai-shi commented Jun 15, 2021

I'm closing this as the original issue is resolved as wont fix.

Please feel free to continue discussion here or open a new issue/discussion.

@dai-shi dai-shi closed this as completed Jun 15, 2021
@dai-shi
Copy link
Member

dai-shi commented Jun 15, 2021

(I'm actually comfortable to revert/change to <Provider createStore={...}>...
Ref #182 #375
It was originally createState and createStore is slightly different.
@Munawwar any thoughts?

specific comment: #182 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants