background
Recently, I used the skeleton screen in the project. Since the project uses the antd component library, I took it for granted.Skeleton Components;
However, when using it, after writing the business code, you still need to deal with the code related to the skeleton screen in a targeted manner.
Purpose
Reduce the development time of the skeleton screen, and it is best to automatically generate the skeleton screen of the related pages.
Technology stack
react @16.8 >=
sass
accomplish
To develop a relatively automated skeleton screen component, you only need to introduce this component into the business component that uses the skeleton screen, and then the corresponding skeleton screen structure will be automatically generated based on the current dom structure.
Here, the blogger has developed a component by himself, which is currently a perfect match for the scenario I use, and will continue to update and iterate according to the business in the future. However, everyone is also welcome to help improve it.
Sample code
The method of using components for the time being is as follows. Of course, if you are interested, you can get the source code and modify it yourself to the desired method.
import { useState, Fragment, useEffect } from 'react'; import { AutoSkeleton, Button } from 'gyp-gao-ui'; export default () => { const [isShow, setIsShow] = useState(true); const [loading, setLoading] = useState(false); const handleFresh = () => { setLoading(true); setTimeout(() => { setLoading(false); }, 2000); }; /** Simulation request */ useEffect(() => { handleFresh(); }, []); return ( <> <Button text="Click to refresh" onClick={handleFresh}></Button> <h1>You can also try to update yourselfdomelement,See if the generated skeleton screen is desirable</h1> <div> {isShow && ( <Fragment> <p>This is a paragraph</p> <p>This is a paragraph</p> <img src="/rmsportal/" /> <div style={{ marginTop: '20px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', }} > <button>show</button> <p>This is a paragraph</p> <img src="/rmsportal/" /> </div> <div> <button>show</button> <p>This is a paragraph</p> <img src="/rmsportal/" /> <div> <button>show</button> <p>This is a paragraph</p> <img src="/rmsportal/" /> <div style={{ marginTop: '20px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', }} > <button>show</button> <p>This is a paragraph</p> <img src="/rmsportal/" /> </div> </div> </div> </Fragment> )} <AutoSkeleton loading={loading} onLoadingSuccess={() => { setIsShow(true); }} onComplete={() => { setIsShow(false); }} /> </div> </> ); };
Core idea
In a small independent component, the corresponding skeleton screen is generated. From the use, we can see that when the blogger is using it, the AutoSkeleton component and the content are isolated through a Fragment component and exist together in a container (div);
Extract the components in the skeleton screen and subdivide the particles. Judging from the commonly used skeleton screens on the market, they are nothing more than two types of text and pictures, and then a series of evolutions are carried out to divide them into different skeleton screen widgets. Here the blogger only divides them into two types of skeleton screen widgets;
The harsh html tags are divided into the above two widgets, and the classification code is
/** is a combination of tag types for text type */ export const textTypes = ['p', 'span', 'a', 'button', 'input', 'textarea', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'li', 'ul', 'ol', 'pre', 'code', 'blockquote', 'cite','strong', 'em','mark', 'del', 'ins','sub','sup','small', 'big'] /** combination of tag types for image type */ export const imageTypes = ['img','svg', 'picture', 'video', 'audio']
Generate a skeleton screen, traversing the content nodes in the container. Different categories and render different widgets. What needs to be handled specifically here is that if the current component is a div and children exist, then the widget will continue to be generated.
Core code
Generate skeleton screen components
import React, { useEffect, useRef, useState } from 'react'; import { comp_className } from '../constants'; import './'; import { Image, Text, imageTypes, textTypes } from './skeleton'; import { nanoid } from 'nanoid'; export interface IAutoSkeletonProps { /** Generate the skeleton screen complete */ onComplete?: () => void; /** * loading * @default true */ loading: boolean; /** Loading complete callback */ onLoadingSuccess?: () => void; } function AutoSkeleton(props: IAutoSkeletonProps) { const { onComplete, loading = true, onLoadingSuccess } = props; const [showMenu, setShowMenu] = useState(false); const [currentPoint, setCurrentPoint] = useState<{ x: number; y: number }>({ x: 0, y: 0, }); const currentRef = useRef<HTMLDivElement>(null); const [skeleton, setSkeleton] = useState<any>(); const genSkeleton = () => { if (!) return; const parent = ; if (!parent) return; /** Elements other than skeleton screen content */ const targetElements = ().filter( (o) => !(comp_className + 'auto-skeleton'), ); const getSkeletonSon = (elements: Element[], parent: Element) => { const child = elements .map((k) => { if ( > 0 && () === 'div') { return getSkeletonSon((), k); } if ((())) { return <Image key={nanoid()} />; } if ((())) { return <Text key={nanoid()} />; } return null; }) .filter((k) => k !== null); const style = getComputedStyle(parent); return ( <div key={nanoid()} style={{ display: 'flex', width: , flexDirection: && === 'flex' ? ( as any) : 'column', justifyContent: , alignItems: , gap: === 'normal' ? '12px' : , }} > {child} </div> ); }; const getSkeletonChild = (elements: Element[]) => { return elements .map((o) => { if ( > 0 && () === 'div') { return getSkeletonSon((), o); } if ((())) { return <Image key={nanoid()} />; } if ((())) { return <Text key={nanoid()} />; } return null; }) .filter((o) => o !== null); }; const skeletonContent = getSkeletonChild(targetElements); setSkeleton(skeletonContent); setTimeout(() => { onComplete && onComplete(); setShowMenu(false); }, 0); }; useEffect(() => { if (loading) { genSkeleton(); } else { onLoadingSuccess && onLoadingSuccess(); } }, [loading]); return ( loading && ( <div className={`${comp_className}auto-skeleton`} ref={currentRef} > {skeleton} </div> ) ); } export default AutoSkeleton;
When it comes to styles, the blogger here also posted the code, and the style of the skeleton screen is still quite complicated.
.xxx-auto-skeleton { width: 100%; height: 100%; display: flex; flex-direction: column; gap: 12px; &-image { flex: 0 0 auto; // Prevent it from being squeezed width: 80px; height: 80px; background: linear-gradient( 90deg, rgba(190, 190, 190, 0.2) 25%, rgba(129, 129, 129, 0.24) 37%, rgba(190, 190, 190, 0.2) 63% ); border-radius: 8px; animation: xxx-skeleton-loading 1.4s ease infinite; background-size: 200% 100%; background-position: 100% 50%; background-position-x: 180%; } &-text { width: 100%; height: 32px; border-radius: 4px; background: linear-gradient( 90deg, rgba(190, 190, 190, 0.2) 25%, rgba(129, 129, 129, 0.24) 37%, rgba(190, 190, 190, 0.2) 63% ); animation: xxx-skeleton-loading 1.4s ease-in-out infinite; background-size: 200% 100%; background-position: 100% 50%; background-position-x: 180%; } @keyframes xxx-skeleton-loading { to { background-position-x: -20%; } } }
The code of widgets is relatively simple, just for subsequent maintenance and is extracted. Here we take Text as an example
import React from 'react'; import { comp_className } from '../../constants'; import '../'; export default function TextSkeleton() { return ( <div className={`${comp_className}auto-skeleton-text`}></div> ) }
Written at the end
If you follow the above carefully, you should be able to have the same effect as the blogger!
However, I am afraid that some friends will be troublesome, so the blogger also thoughtfully released my component library and can download it directly in the npm repository
npm i -S gyp-gao-ui
This is the end of this article about the sample code of React implementing the skeleton screen. For more related React skeleton screen content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!