examples or proper ways to convert useState + useEffect to RSC + Next.js 13?
#47227
-
SummaryI am struggling to figure out how to convert the normal way we do Before RSC, Ideally, in the RSC world, I would imagine moving the How should I be solving this or is there an example of how this is solved? Also, I do not understand why i cannot call the Thank you in advance. Additional informationNo response ExampleNo response |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 10 replies
-
|
Interactivity and RSC are part of the rough edges. It would seem that this is one of the cases, where you have to escape-hatch from RSC into 'use client', because the user selected dropdown value has to be used to fetch data. Image if you could have a client component that just renders the dropdown and keeps track of the selected value, and that you could pass the selected value as props onto a server component. This is an interesting challenge, because you cannot have direct RSC children to a client component, rather you have to use composition, https://beta.nextjs.org/docs/rendering/server-and-client-components#importing-server-components-into-client-components.
So, I think, you have to keep the Alternatively, you could add the selected value to the URL as a search param Alternatively, you could use the current Here's a small example: import { Counter } from "@/components/Counter";
import { DropDown } from "@/components/Dropdown";
// This part is important!
export const dynamic = "force-dynamic";
const fetchPokemon = async (poke: string) => {
if (!poke) return null;
try {
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${poke}`);
if (!response.ok) return null;
const data = await response.json();
return data;
} catch (reason) {
return null;
}
};
export default async function Foo({
searchParams,
}: {
searchParams?: { [key: string]: string | string[] | undefined };
}) {
const selectedSearch = searchParams?.selected ?? "";
const selected = Array.isArray(selectedSearch)
? selectedSearch[0]
: selectedSearch;
const pokemon = await fetchPokemon(selected);
return (
<>
<Counter />
<DropDown selected={selected || ""} />
{pokemon ? (
<section>
{pokemon.name} - {pokemon.id}
</section>
) : (
<p>Select a pokemon</p>
)}
</>
);
}And then the dropdown: "use client";
import type { ChangeEvent } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
const options = ["mew", "mewtwo", "pikachu"];
export const DropDown = ({ selected }: { selected: string }) => {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const onSelect = (event: ChangeEvent<HTMLSelectElement>) => {
const current = new URLSearchParams(searchParams);
const value = event.target.value.trim();
if (!value) {
current.delete("selected");
} else {
current.set("selected", event.target.value);
}
const search = current.toString();
const query = search ? `?${search}` : "";
router.push(`${pathname}${query}`);
};
return (
<select value={selected} onChange={onSelect}>
<option value="">None</option>
{options.map((opt) => (
<option key={opt} value={opt}>
{opt}
</option>
))}
</select>
);
};And the counter: "use client";
import { useState } from "react";
export const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount((x) => x + 1)}>{count} + 1</button>
</div>
);
};And it works like this (production mode): Screen.Recording.2023-03-17.at.08.46.35.mov
You cannot call it directly as Just call the data you need from the RSC! These routes are meant to be outward facing APIs to server data for your client, and other consumers of non-pages data, like robots, sitemaps, etc. |
Beta Was this translation helpful? Give feedback.
-
|
Separating server and client components is pure crap!! Go with NodeJs if you can! Stop the Fullsatck lie, we're wasting our time... |
Beta Was this translation helpful? Give feedback.
-
|
Any new approaches to this?
This will make the full page dynamic. I would like to use SSG for pages without a search para and in case of search params to fall back to SSR. Is that somehow possible? |
Beta Was this translation helpful? Give feedback.
-
|
So I don't know if it is a bug but I managed to load a RSC in a client component using suspense after changing a state. This is the client components, that is loading a server component passing the value from the state as props: "use client";
import { Suspense, useState } from "react";
import { ServerComp2 } from "./ServerComp2";
export const ClientComp = () => {
const [value, setValue] = useState("demo");
return (
<div>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<Suspense fallback={<div>Loading...</div>}>
<ServerComp2 value={value} />
</Suspense>
</div>
);
};This is the server component, receiving the new state (note the "use server";
import { readFile } from "fs/promises";
export const ServerComp2 = async ({ value }: { value: string }) => {
// read tsconfig content
const tsconfig = await readFile("tsconfig.json", "utf8");
console.log("My server component", { tsconfig });
// Do some slow magic
await new Promise((resolve) => setTimeout(resolve, 500));
console.log("My server component timer done");
return (
<div>
My server component2 with val: {value} <code>{tsconfig}</code>
</div>
);
};Having Find full example here https://github.com/apiel/demo-rsc I think one problem of this solution is that server action doesn't have cache mechanism, therefor the component will have to be loaded on every state change, even if it was already loaded previously. I couldn't find any official doc about this, so I would not rely on this solution :-/ however it is working :p To avoid to the error: export const ClientComp = () => {
const [value, setValue] = useState<string | undefined>(undefined);
useEffect(() => {
setValue("demo");
}, []);
return (
<div>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
{value !== undefined && (
<Suspense fallback={<div>Loading...</div>}>
<ServerComp2 value={value} />
</Suspense>
)}
</div>
);
}; |
Beta Was this translation helpful? Give feedback.
Interactivity and RSC are part of the rough edges.
It would seem that this is one of the cases, where you have to escape-hatch from RSC into 'use client', because the user selected dropdown value has to be used to fetch data.
Image if you could have a client component that just renders the dropdown and keeps track of the selected value, and that you could pass the selected value as props onto a server component. This is an interesting challenge, because you cannot have direct RSC children to a client component, rather you have to use composition, https://beta.nextjs.org/docs/rendering/server-and-client-components#importing-server-components-into-client-components.