There are multiple places where we load data asynchronously in TheGoodPsy, 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 the render prop.
<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>