I’m trying to create a library to generate skeleton-loading components on the fly. I like those skeletons components with a shimmer effect, but I don’t want to create them manually. So my idea is something like this:
I create a component which receives the component that I want to load as a children
while loading, I render a copy of this component, but invisible
I iterate through the component elements, and render a skeleton component based on that
I’ve already made some progress so far, I created a nextjs boilerplate just for testing purposes, I’m new at opensource, and I will create a library with typescript and rollup when I’m done testing.
My code by now is in this repository: https://github.com/FilipePfluck/skeleton-lib-test
here is the core component:
const Shimmer = ({ children, isLoading, component: Component, exampleProps }) => {
const fakeComponentRef = useRef(null)
useEffect(()=>{
console.log(fakeComponentRef.current.children)
},[fakeComponentRef])
const texts = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'strong']
const contents = ['img', 'video', 'button']
const styleProps = ['borderRadius', 'padding', 'margin', 'marginRight', 'marginLeft', 'marginTop', 'marginBottom', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight', 'display', 'alignItems', 'justifyContent', 'flexDirection']
const renderElement = (element) => {
console.log('renderElement')
const object = {}
styleProps.forEach(s => Object.assign(object, {[s]: element.style[s]}))
if(texts.includes(element.localName)){
const fontSize = +document.defaultView.getComputedStyle(element, null)["fontSize"].replace('px','')
const lineHeight = +document.defaultView.getComputedStyle(element, null)["lineHeight"].replace('px','') | fontSize * 1.2
const numberOfLines = Math.round(element.offsetHeight / lineHeight)
const lineMarginBottom = lineHeight - fontSize
const lines = []
for(let i=0; i<numberOfLines; i++){
lines.push(i)
}
return(
<div style={{display: 'flex', flexDirection: 'column'}}>
{lines.map(line => (
<div
style={{
width: element.offsetWidth,
...object,
height: fontSize,
marginBottom: lineMarginBottom
}}
className="shimmer"
key={"line"+line}
/>))}
</div>
)
}
if(contents.includes(element.localName)){
return (
<div
style={{
width: element.offsetWidth,
height: element.offsetHeight,
...object
}}
className={'shimmer'}
/>
)
}
return (
<div
style={{
width: element.offsetWidth,
height: element.offsetHeight,
display: element.style.display,
alignItems: element.style.alignItems,
justifyContent: element.style.justifyContent,
flexDirection: element.style.flexDirection,
padding: element.style.padding,
margin: element.style.margin
}}
>
{!!element.children
? [...element.children]
.map(child => renderElement(child))
: null
}
</div>
)
}
return isLoading ? (
<>
<div style={{visibility: 'hidden', position: 'absolute'}} ref={fakeComponentRef}>
<Component {...exampleProps}/>
</div>
{fakeComponentRef?.current && renderElement(fakeComponentRef.current)}
</>
) : children
}
Basically I’m doing a recursive function to render the skeleton component. But I got a problem: the loading component is taking too long to render. Sometimes it takes over 40 seconds. And this is a massive problem, a loading component should not take longer to render then the loading itself. But I have no clue why is it taking so long. Also, I know that there is a lot of other problems, but my first goal was to render the component on the screen. I’d be pleased if any of you wanted to contribute to my project