Create a dynamic component
For a component to have dynamic behavior -that is, to being able to update or re-render its contents without reloading the whole page- it needs to be explicitely configured as a dynamic component.
Currently, creating a dynamic process is a bit more convolved process than creating a static component, and it requires the usage of the GetDynamicComponent
helper function:
export const MyDynamicComponent = GetDynamicComponent<MyData,SXLGlobalContext>(
"my-dynamic-component",
async () => {
const data = await getData()
return data;
},
(data, props) => {
if (data.isPending) {
return <>Loading...</>;
}
return <div>Loaded: {data.value}</div>
},
);
// ...
<MyDynamicComponent.Render />
The GetDynamicComponent
function receives three arguments:
- Component ID (
string
): A unique ID used to identify the dynamic component. - Data Fetcher (
() => MyData
): A function that returns aPromise
with the data the component needs to fetch. The type for the Promise contents is passed in theGetDynamicComponent
first type parameter (e.g.MyData
). - Render function: (
(data:TrackedPromise<MyData>, props: SXL.Props) => JSX.Element
): A function that receives the promise returned by the data fetcher, wrapped inTrackedPromise
-which is an extension forPromise
that exposes methods for tracking its state-, and the props passed to the component.
Updating a dynamic component
All dynamic components can be updated using the webAction helper:
<button
onclick={webAction({}, (ev, webContext) => {
webContext?
.actions?
.refetchElement("my-dynamic-component", {
// pass query parameters
});
})}>
Reload
</button>
This helper creates all bindings necessary to update a dynamic component.
The refetchElement
receives two parameters:
- Component ID (
string
): The ID for the component to update - Query parameters (
Record<string, string>
): A map of query parameters for the updated component.
Example: Rendering the server's date
Let us create an example to understand how to update dynamic components.
First, we will create a dynamic component called ServerDateComponent
:
export const ServerDateComponent = GetDynamicComponent<
Date,
SXLGlobalContext & { mmDDYY?: boolean }
>(
"my-server-date-component",
async () => {
const serverDate = await getServerDate();
return serverDate;
},
(data, props) => {
if (data.isPending) {
return <>Loading...</>;
}
const serverDate: Date = data.value;
if (props?.globalContext?.mmDDYY) {
const dateFormatted = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "2-digit",
day: "2-digit",
}).format(serverDate);
return <div>Server date: {dateFormatted}</div>;
}
return <div>Server date: {serverDate.toISOString()}</div>;
},
);
// ...
<ServerDateComponent.Render />
The ServerDateComponent
does a couple of things:
- Two type parameters are passed to the
GetDynamicComponent
signature:Date
: The type for the data returned by the fetcher.SXLGlobalContext & { mmDDYY?: boolean }
: An extension of the global contextSXLGlobalContext
with the boolean query parametermmDDYY
.
- We pass a fetcher function that retrieves a date from the server using
getServerDate()
. - While the component is fetching the server date, a
Loading
message will be displayed. - Once the promise is resolved, the server
Date
object is retrieved usingdata.value
. - Then, depending whether
mmDDYY
istrue
orfalse
, it renders a string representation either in formatmm/dd/yy
or ISO.
Note: All this logic happens on the server side. The dynamic component on the browser-side will receive the fully rendered HTML content returned by the component. Any temporary state contained by the component on the browser (e.g. the text in any input field) will be discarded if it's not explicitely sent to the server as a query parameter.
Now, let's create a second static component which will update the contents of our dynamic ServerDateComponent
:
export function ReplacerComponent() {
return (
<>
<button
onclick={webAction({}, (ev, webContext) => {
webContext?.actions?.refetchElement(
"my-server-date-component",
{
mmDDYY: true,
},
);
})}
>
Get server date on mm/dd/yyyy format
</button>
<button
onclick={webAction({}, (ev, webContext) => {
console.log("Replace");
webContext?.actions?.refetchElement(
"my-server-date-component",
{},
);
})}
>
Get server date on ISO format
</button>
</>
);
}
// ...
<ReplacerComponent/>
Notice a few things here:
- Static components can use the
webAction
helper. A component doesn't need to be a dynamic component to update another dynamic component. - The component renders two buttons, both of which updates the dynamic component using its ID (
"my-server-date-component"
):- The first button passes the
mmDDYY
query parameter set totrue
. - The second button passes no query parameters, which will re-render the dynamic component using its default state (rendering the date in ISO format).
- The first button passes the
After rendering both components, we can see the dynamic component being updated correctly:
Configuring query params in the middlware
Currently, query parameters like mmDDYY
need to be configured in LeanJSX middleware to be parsed from the request query parameters if they are not part of the global context:
app.use(
LeanApp.middleware({
configResponse: (resp) => resp.set("Content-Security-Policy", CSP),
globalContextParser: (req, componentId) => {
if (componentId === ServerDateComponent.contentId) {
return {
...parseQueryParams(req),
...{
mmDDYY: Boolean(req.query?.mmDDYY),
},
};
}
return parseQueryParams(req);
},
}),
);
We add a check in globalContextParser
to see if componentId
matches our component's ID, and if it does, we parse the mmDDYY
query param. This allows us have query params that are specific to a component without polluting the global context.
Note: The middleware configuration may not be needed in future versions of LeanJSX.