Sharing data across client & server components?

I am trying to understand how I am supposed to share data across my components. What I am trying to do is have a auth_token saved in memory that I can share across my application. Right now It feels like I have two options 1) A context provider 2) prop drilling Mixing both of these options seems to get me what I want. My issues is that my gut tells me I am doing things the wrong way. I have a small sandbox set up for testing where I have a few client and server components that I use to test if data is being passed around. page.tsx
import React from 'react';
import ContextCC from "./contenxt_cc";
import ContextSC from "./context_sc";
import ContextChildCC from './context_child_cc';

const Page: React.FC = () => {
const data = "We shared data!";
return (
<div>
<p> This is a server component - {data} </p>
<ContextCC data={data}>
<ContextSC data={data} />
<ContextChildCC />
</ContextCC>
</div>
);

};

export default Page;
import React from 'react';
import ContextCC from "./contenxt_cc";
import ContextSC from "./context_sc";
import ContextChildCC from './context_child_cc';

const Page: React.FC = () => {
const data = "We shared data!";
return (
<div>
<p> This is a server component - {data} </p>
<ContextCC data={data}>
<ContextSC data={data} />
<ContextChildCC />
</ContextCC>
</div>
);

};

export default Page;
context_cc.tsx
'use client'
import React, { useContext } from 'react';
import AppContext from './app_context';

export default function ContextCC({ children, data }: { children: React.ReactNode, data: string }) {
const sharedData = {
sharedMessage: data,
}
return (
<AppContext.Provider value={sharedData}>
<div>
<p> This is a client component - {sharedData.sharedMessage} </p>
{ children }
</div>
</AppContext.Provider>
);
}
'use client'
import React, { useContext } from 'react';
import AppContext from './app_context';

export default function ContextCC({ children, data }: { children: React.ReactNode, data: string }) {
const sharedData = {
sharedMessage: data,
}
return (
<AppContext.Provider value={sharedData}>
<div>
<p> This is a client component - {sharedData.sharedMessage} </p>
{ children }
</div>
</AppContext.Provider>
);
}
context_sc.tsx
export default function ContextSC({data}: { data: string }) {
return (
<div>
<p> This is a server component - { data }</p>
</div>
);
}
export default function ContextSC({data}: { data: string }) {
return (
<div>
<p> This is a server component - { data }</p>
</div>
);
}
contect_child_cc
"use client";
import { useContext } from "react";
import AppContext from "./app_context";
import { printAuthToken } from "./server_action";
import { useState } from "react";

export default function ContextChildCC() {
const [serverRes, setServerRes] = useState("");
const context = useContext(AppContext);
const action = async () => {
const result = await printAuthToken(context.sharedMessage)
setServerRes(result.msg);
}
return(
<div>
<p> This is a child client component - {context.sharedMessage} </p>
<p> {serverRes} </p>
<button onClick={action}> Send </button>
</div>
)
}
"use client";
import { useContext } from "react";
import AppContext from "./app_context";
import { printAuthToken } from "./server_action";
import { useState } from "react";

export default function ContextChildCC() {
const [serverRes, setServerRes] = useState("");
const context = useContext(AppContext);
const action = async () => {
const result = await printAuthToken(context.sharedMessage)
setServerRes(result.msg);
}
return(
<div>
<p> This is a child client component - {context.sharedMessage} </p>
<p> {serverRes} </p>
<button onClick={action}> Send </button>
</div>
)
}
This gives me the output that I would expect. It just feels like there should be a smarter way to pass data around. Like If I have a server component that would need to pass along a auth-token I don't like having to ensure that I am passing the prop. I feel like I could easily forget to do it and brake things. Any ideas or tips would be super helpful thanks ❤️ Update: I also made a server action and I feel like this flow is better. Is the way to go just use server actions over server components? Maybe what I am trying to understand better is when to use server components over server actions over client components? This is all kinda confusing to me >,<
"use server"

export async function printAuthToken(authToken: string) {
console.log(authToken);
return { msg: "This is a response from a server action - " + authToken }
}
"use server"

export async function printAuthToken(authToken: string) {
console.log(authToken);
return { msg: "This is a response from a server action - " + authToken }
}
No description
2 Replies
ideceddy
ideceddyOP10mo ago
No description
cupofcrypto
cupofcrypto10mo ago
I faced a similar issue recently implementing auth with a custom BE. In the end I solved leveraging httpOnly cookies... On the client, with Redux, I let the user login into the app. Once that's done, I use the cookie for the following REST calls and I pass the data down to the client when needed.
Currently I am thinking of a refactor to test few ideas, but I also have to ship new features so we will see 😅
Want results from more Discord servers?
Add your server