Next.js Discord

Discord Forum

Sharing state between a client and server component

Answered
Cape lion posted this in #help-forum
Open in Discord
Cape lionOP
I have a client component called ImageViewer which takes in some props to render an image viewer on the screen.
export default function ImageViewer({
    isOpen,
    close,
    url
}: {
    isOpen: boolean;
    close: () => void;
    url: string;
}) {
  // ...
}

The image viewer can be opened externally at any point in time, therefore the visibility of the viewer is controlled by the isOpen prop. When the image viewer is opened it is possible to close it by clicking on an element within, therefore a close prop is passed in which is called when the image viewer should be closed. The problem I'm facing is that I cannot pass in the function () => setViewerOpen(false) as the close prop from the server component because functions are not serializable as far as I know. How can I solve this problem?
Answered by Asian black bear
you export it from another file as like <GalleryOpenerProvider> that is use client
View full answer

49 Replies

Asian black bear
The server is stateless, so even if that callback was possible, there is nothing to set! Maybe try wrapping the server component you want to show/hide in a client component that shows/hides it using state?
Or even use client components entirely for that, with maybe passing in server-side props only with a server component
If the server knowing is really important there are always query parameters in the url and cookies
@Asian black bear The server is stateless, so even if that callback was possible, there is nothing to set! Maybe try wrapping the server component you want to show/hide in a client component that shows/hides it using state?
Cape lionOP
When you open the image viewer it is going to take up the full screen, and this image viewer is used on multiple pages, so the parent here is actually a layout.tsx, where do you think would be a more appropriate place? Thanks a lot by the way for helping out!
Asian black bear
ummm, if I really wanted it to be present and not re-render through page changes, I would put it in the layout, in around the same place as a navbar
Cape lionOP
Asian black bear
No, I would make a context to open it with, and in the provider wrapper I would inject the actual dialog that opens too
@Asian black bear ummm, if I really wanted it to be present and not re-render through page changes, I would put it in the layout, in around the same place as a navbar
Cape lionOP
I don't have any issues with it re-rendering, I just thought that it being in the layout made the most sense
Asian black bear
it can use the context too 😉
Cape lionOP
oh you can modify the context from a child component?
Asian black bear
so, one important conceptual thing, is that if you define the context wrapper and component in a separate use client file, it will not force all of the children to be use client
@Cape lion oh you can modify the context from a child component?
Asian black bear
Any child of the provider can use the context, including the one that it is supposed to be controlling
Asian black bear
The answer from Catraco is pretty straightforward, and is absolutely what I would do
with maybe some proxying for the setter to make it work nice with typescript
because what is the type, really, of a useState setter
@Asian black bear The answer from Catraco is pretty straightforward, and is absolutely what I would do
Cape lionOP
oh I see, though in my situation I can't use useState in layout.tsx because it is server rendered yeah?
Asian black bear
you export it from another file as like <GalleryOpenerProvider> that is use client
Answer
Asian black bear
and just wrap it around the layout in the layout.tsx
@Asian black bear you export it from another file as like `<GalleryOpenerProvider>` that is `use client`
Cape lionOP
oh gotcha, I will try this out, thanks again for the help!
Asian black bear
the context will be invisible from server components basically, like if you put an open button component places that button will need to be use client
@Asian black bear the context will be invisible from server components basically, like if you put an open button component places *that button* will need to be `use client`
Cape lionOP
hmm, right, I think my brain's gonna need some time to process exactly what's going on 😄
Asian black bear
the entire page can be a server component, with just that one use client button
There is an inspirational talk about pushing client components to the very leaves of your document tree
if you don't mind, can you send the talk? I think I would enjoy watching it
Asian black bear
let me see if I can remember where--it was in the official hype for the /app dir
Now that you have a prototype of how what you want to do is very possible with state, the official docs actually do tell you exactly how too! https://nextjs.org/docs/getting-started/react-essentials#context
a kinder, gentler, RTFM 🙂
read the friendly manual! a friendly reminder!
@Asian black bear a kinder, gentler, RTFM 🙂
Cape lionOP
haha
@Asian black bear you export it from another file as like `<GalleryOpenerProvider>` that is `use client`
Cape lionOP
wait I don't think I still fully understand how to implement this idea, would this not mean that the children of the body are client rendered?
so the { children } of the layout.tsx canbe reused for the page of the same route segment or used for child layouts on child route segments
Asian black bear
They only become use client by being in that file. The cooties don't travel through {children}
@alfon so the { children } of the layout.tsx canbe reused for the page of the same route segment or used for child layouts on child route segments
Cape lionOP
oh I think you misunderstood, that was not what I was confused about
@Cape lion oh, that's cool!
Asian black bear
Honestly, I thought the /app dir was a terrible idea until I understood that
like, it breaks/changes everything!
@Asian black bear Honestly, I thought the /app dir was a *terrible* idea until I understood that
Cape lionOP
yeah, without this client components become viral 🤢
Asian black bear
Let me know how your project goes! Pretty soon I will be refactoring an app that uses Redux and putting all of this to some kind of extreme test.
I might fall in love with Redux again
Cape lionOP
I haven't tried out Redux or anything too React
came from a SvelteKit for the most part
Cape lionOP
Works beautifully, thank you so much @Asian black bear 🙂
Asian black bear
nice!