Render a loader contidionally in React with a High Order Component
There are multiple places where we load data asynchronously in TheGoodPsy's web application, so we use the ReactLoading package to show a spinner while the data is being downloaded and processed in the frontend.
There is a common block of code that gets repeated:
{loading ? (
<ReactLoading
className="chat-loader"
type="spin"
color={COLOR} />
) :
<>whatever content here</>
}
Sometimes the loader needs to be wrapped in a different html skeleton, being the perfect place to use a render prop. I took a bit of time to myself to write a simple HOC that would show a spinner based on loading property, and can wrap the loader using a custom template from a render prop.
const WithLoader = ({
loading,
loadingTemplate,
style,
height,
width,
color,
children
}) => {
const internalLoadingTemplate = () => {
const loader = (
<ReactLoading
{ ...style }
height={height || 16}
width={width || 16}
type="spin"
color={color || CUSTOM_COLOR} />
);
return loadingTemplate ?
loadingTemplate(loader) :
<>{ loader }</>;
}
return (
loading ?
internalLoadingTemplate() :
<> {children} </>
);
};
export default WithLoader;
So in order to use it without any defaults (other than loading) is for example:
<WithLoader loading={true}>
<span onClick={joinCall}>
join call
</span>
</WithLoader>
And if we want to wrap the loader in a custom format we use a render prop. Important to note we need to explicitly render the loader within it.
<WithLoader
loading={true}
loadingTemplate={(loader) => {
return (
<div classNames="custom-loader">
<span>
{ loader}
</span>
</div>
)
}}>
<span
className="join-call-link"
onClick={(e)=> joinCall(e, roomSid, isAudioOnly)}>
{ t('JOIN_CALL_JOIN') }
</span>
</WithLoader>