#Sharing data upstream or between client components with a different parent server component

24 messages · Page 1 of 1 (latest)

vernal hinge
#

Hi, we have a layout at /[market]/[language] level, this layout has the website header and footer. This header has a language switch. The language switch needs to have data that's page-specific / only available at /[market]/[language]/[[slug]]level. How can we propagate this data upstream?

robust shoal
#

You can use

  • useContext hook
  • Redux/Redux toolkit
  • ReactQuery (insert data in child component and consume it at parent component)

I would recommend useContext.

gentle nest
#

+1 for useContext

vernal hinge
#

can data be communicated upstream through server components by useContext?

#

what would that look like? what value do I provide?

#

in a traditional react app without server components I know I can useState and put value + setter into the context value, but I cant do that if im providing this context in a server component

gentle nest
#

No, server components by design encourage fetching all the data they need in the component itself and make use of the caching features instead of passing data to them.

#

Context does not work with server components.

vernal hinge
#

the actual design is more like...
Part 1:
app/[market]/[language]/layout.tsx -> html > body > header > <languageselect> -> components/header/LanguageSelect.tsx
Part 2:
app/[market]/[language]/[[slug]]/page.tsx -> getMetadata() (or whatever that's called) provides the alternate URLs that I also need in language select

#

but that leaves me with no native way to move that data upstream I think

gentle nest
#

Make components/header/LanguageSelect.tsx a client component.
Export:

  1. LanguageMeta: A component that accepts your metadata as props and uses useContext to set it in a useEffect. The component can just return null.
  2. LanguageMetaProvider: A component, which simply wraps the context provider
  3. Your LanguageSelect component

In app/[market]/[language]/layout.tsx:

<LanguageMetaProvider>
  //...
  <LanguageSelect/>
  //...
  {children}
  //...
</LanguageMetaProvider>

In app/[market]/[language]/[[slug]]/page.tsx:

<LanguageMeta meta={getMetadata()}/>
#

Due to all 3 being exported from a client file, this works in server components.
LanguageMeta returns null, because it just acts as a wrapper to use the hook inside of a server component.

vernal hinge
#

ooh I think I get it, this is using that server components can be used as children for client components. What would happen if I stored it in a ref instead of state?

#

(and set it first render instead of in an effect), would that just not work, or would it depend on 'execution order'?

#

I suspect the execution order would always be unfavorable, if it did matter 😦

gentle nest
# vernal hinge ooh I think I get it, this is using that server components can be used as childr...

Updating the ref won't rerender the page. An effect is necessary because it would cause a mismatch between server and client during hydration otherwise.

You can provide an initial value in your layout, which is then updated on the client in useEffect.
The effect and context are necessary because of client-side navigation. Without it, the metadata wouldn't change after the initial page load. Shared layouts are not updated on client-side navigation.

#

Though, maybe there's something else you can do, depending on how the getMetadata function works.

#

How does the function know which page you're on? Are you providing any parameters?

vernal hinge
#

yeah the slug param

#

I have the setup with useEffect now, but still really would like to do this on initial render

vernal hinge
#

maybe using <Template>?

#

or, parallel routes probably

gentle nest
# vernal hinge yeah the slug param

This makes it easier. You won't need any context in this case, unless you want to pass this data down to somewhere.

Params are passed to the layout as a prop. You can directly use your getMetadata function with the slug in the layout server component and pass it to the LanguageSelect. The slug in the layout won't change after client-side navigation, so this is just for the initial value.
Inside your LanguageSelect component, you can use useParams to update the data in a useEffect on client-side navigation.

This requires your function to be working on both server and client. If it doesn't work on the client, you can create a route handler and fetch it from the client.

vernal hinge
#

I'm somewhat confident that I wasnt getting the slug param in layout (thet [[slug]] directory has the same parent directory as this layout.tsx does)
And we're using ISR, so the "initial page load" almost certainly would be re-using the layout from a different page.
However, I am somewhat confident that paralel routes will actually do the trick