Reputation: 510
I'm trying to build a single page app that uses only one large back end procedure. The procedure is triggered by only one input that is a string. Stages take and result in:
I want to make the page render the stuff gradually as soon as new parts are ready. All the parts 1-4 of the back end procedure cannot be implemented in Next.js server actions themselves yet can be triggered sequentially by one or several server actions. Each stage is dependent on the previous.
Now I know how to render only one part at a time making one call to a Next.js server function and revalidating path /. I do not understand how to organize other parts and make dependencies between them. Whether I should have 4 separate states and 4 separate server functions is not clear to me. My Next.js server functions call other backends implemented in other technologies and waiting for those parts to complete. Those other technologies backends are not exactly relevant to the question and can be considered/mocked as sleep periods of server functions.
What I currently have is:
'use client'
import { serverAction } from "./serverStuff";
import React, { MouseEventHandler } from "react";
var someStuff = [""]
const handleString: MouseEventHandler<HTMLButtonElement> = async (e) => {
e.preventDefault();
await serverAction("my-input-string").then( // my-input-string is hardcoded for now
function(value) {someStuff = value}
);
}
export default function Home() {
return (
<>
<div className="main">
<div>
<button type="button" onClick={handleString}>
</button>
</div>
<div>
<ul>{someStuff.map( (stf) => (
<li key={stf}>{stf}</li>
))}
</ul>
</div>
</div>
</>
);
}
'use server'
export async function serverAction(input: String): {
// processing is here
revalidatePath("/");
return [ "some", "stuff" ]; // list result is currently hardcoded as well
}
If you know how to do such cascade multi-stage rendering in React/Node.js, please share your approaches with some code snippets. I not necessarily need all the 4 stages shown implemented. I just cannot get how to weave sequentially rendering of different parts of the page so if you can share code of at least two stages woven sequentially please share.
My best idea for now is to have 3 same level elements (marks are made on top of previously rendered image/diagram), 4 server functions and 4 revalidatePath calls.
P.S. I am not sure if the weaving related part of my question is React/Next.js specific or a general JavaScript part and can be solved with vanilla JavaScript.
Upvotes: 0
Views: 229
Reputation: 111
I am not entirely sure i understood everything, but here's my attempt to cascade stages with next.js server actions.
I have used one state variable to store the name of the current stage and the output received from it.
The event handler runs just the first stage,
then i use the useEffect
to run the subsequent stages. the final stage should return 'stage-1' as the stage name or an additional if statement can be added to the useEffect function identify the end of all the stages successfully.
// Home.tsx
"use client";
import { serverAction } from "./serverStuff";
import React, { MouseEventHandler, useEffect, useState } from "react";
export default function Home() {
const [state, setState] = useState<{ stage: string; data: string[] }>({
stage: "stage-1",
data: [],
});
const handleString: MouseEventHandler<HTMLButtonElement> = async (e) => {
e.preventDefault();
if(state.stage==='stage-1') await runStage(state.stage, "my-input-string");
};
async function runStage(stage: string, input: string) {
const newState = await serverAction({ stage: stage, input: input });
setState(newState);
}
useEffect(() => {
async function handleStageChange() {
if (state.stage === "stage-1")
return; //only execute next stage when stage-1 has succeeded
else {
await runStage(state.stage, "input-for-next-stage");
}
}
handleStageChange();
}, [state]);
return (
<>
<div className="main">
<div>
<button
type="button"
onClick={handleString}
style={{
marginTop: "1rem",
padding: "1.5rem",
borderRadius: "10px",
background: "blue",
}}
>
Click ME
</button>
</div>
<div>
<ul>
{state.data.map((stf, index) => (
<li key={index}>{stf}</li>
))}
</ul>
</div>
</div>
</>
);
}
// serverStuff.ts
"use server";
export async function serverAction(formData: {
stage: string;
input: string;
}): Promise<{ stage: string; data: string[] }> {
let nextStage = "stage-2";
// processing based on stage value
// run if...else or switch statement and perform correct action
if (formData.stage === "stage-1") {
// perform stage 1 function
nextStage = "stage-2"; //set next stage
}
// revalidatePath("/") // required when you want update/re-render a different page,(page other than current form) based on the form submission as well
return { data: ["some", "stuff"], stage: nextStage }; // list result is currently hardcoded as well
// return result of processing and name/label for next stage
}
Ps - typed this on my phone might have some silly syntax errors, thanks let know if this worked
Upvotes: 0