Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Hybrid Placeholder
import React, { useEffect, useState } from 'react';
import { withSitecoreContext, dataApi, Placeholder } from '@sitecore-jss/sitecore-jss-react';
import { dataFetcher } from './dataFetcher';
import config from './temp/config';
const HybridPlaceholder = ({
name,
rendering,
sitecoreContext,
}) => {
const {
route,
pageEditing,
} = sitecoreContext;
const [isFetched, setIsFetched] = useState(false);
// Used to fetch the placeholder data with specific parameters.
const fetchPlaceholder = () => dataApi.fetchPlaceholderData(
name,
route?.itemId,
{
layoutServiceConfig: {
host: config.sitecoreApiHost,
},
querystringParams: {
sc_lang: route?.itemLanguage,
sc_apikey: config.sitecoreApiKey,
isHybridPlaceholder: true,
},
fetcher: dataFetcher,
},
);
// Will add the isLoaded prop to all components.
const addIsLoadedProp = (isLoaded, elements) => {
if (Array.isArray(elements)) {
elements.forEach(({ fields }) => {
if (fields) {
fields.isLoaded = isLoaded;
}
});
}
};
// Only fetch the placeholder data when we navigate to a new page.
// Since useEffect does not work server-side we don't need a client-side check.
useEffect(() => {
if (!pageEditing && rendering?.placeholders?.[name]) {
setIsFetched(false);
fetchPlaceholder()
.then(result => {
addIsLoadedProp(true, result.elements);
// Override all components in the placeholder with the new data.
// This data contains the heavy code.
rendering.placeholders[name] = result.elements;
setIsFetched(true);
}).catch(error => {
console.error(error);
});
}
}, [route?.itemId]);
if (!pageEditing
&& !isFetched
&& rendering?.placeholders?.[name]) {
addIsLoadedProp(false, rendering.placeholders[name]);
}
return (
// Render the first time without the heavy data.
// Render a second time with all the data loaded.
<Placeholder name={name} rendering={rendering} />
);
};
export default withSitecoreContext()(HybridPlaceholder);
const PromoBlock = (props) => {
const {
image,
richText,
heading,
button,
isLoaded
} = props;
return (
<Theme>
<Layer>
<Retain>
{ isLoaded && (
<p>Date: {props.date}</p>
)}
{ !isLoaded && (
<p>Date: Loading...</p>
)}
<div className="c-promoblock">
<div className="o-layout o-layout--gutter">
<div className="o-layout__cell u-fraction--1/2@from-lap">
<Img image={image} className="u-m-b" />
</div>
<div className="o-layout__cell u-fraction--1/2@from-lap">
<Heading text={heading.text} level={heading.level} className="u-m-t-tiny" />
<Rte richText={richText} />
{button ? (
<Button tag="button" modifier="secondary" field={button} />
) : null}
</div>
</div>
</div>
</Retain>
</Layer>
</Theme>
);
};
public class PromoRenderingContentsResolver : IRenderingContentsResolver
{
private readonly Func<IMvcContext> contextThunk;
private readonly IMapper mapper;
public PromoRenderingContentsResolver(Func<IMvcContext> contextThunk, IMapper mapper)
{
this.contextThunk = contextThunk;
this.mapper = mapper;
}
public override object ResolveContents(Rendering rendering, IRenderingConfiguration renderingConfig)
{
var context = this.contextThunk();
var datasource = context.GetDataSourceItem<Promo>();
var model = this.mapper.Map<PromoJsonDto>(datasource);
if (this.IsHybridPlaceholder)
{
// Here the heavy code can be executed which will be done async.
// So after the page is already loaded this will be added afterwards.
Thread.Sleep(2000);
model.Date = DateTime.Now.ToString("f");
}
return model;
}
private bool IsHybridPlaceholder
{
get
{
bool.TryParse(HttpContext.Current?.Request?.QueryString?["isHybridPlaceholder"], out var isHybridPlaceholder);
return isHybridPlaceholder;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment