需要
家喻户晓,利用如果少了loading
,交互就显得生硬。
本文分享如何在React
中从零到一实现并应用loading
。
实现
一个loading
,应该始终呈现在视口的正中。
同时为了示意加载过程的动态性,须要适当的动画。
以及,胜利和失败的提醒与loading
其实实质上是一回事,所以,实现loading
的同时,也顺便将另外两个一并实现。
严格意义上,这是toast
的实现。
Toast.jsx
import l from './toast.module.css'
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
import {faCheck, faSpinner, faTriangleExclamation} from '@fortawesome/pro-solid-svg-icons'
import PropTypes from 'prop-types'
import {memo} from 'react';
function Toast({type, message}) {
return (
<div className={l.toast} >
<div className={l.container} >
{
type === 'success'
? <FontAwesomeIcon icon={faCheck} />
: type === 'fail'
? <FontAwesomeIcon icon={faTriangleExclamation} />
: <FontAwesomeIcon icon={faSpinner} className={l.loading} />
}
</div>
{
message
? message
: type === 'success'
? '实现'
: type === 'fail'
? '失败'
: '加载中'
}
</div>
)
}
Toast.propTypes = {
type: PropTypes.oneOf(['loading', 'success', 'fail']),
message: PropTypes.string
}
export default memo(Toast)
toast.module.css
:root {
--toastSize: 136px;
--containerSize: 40px;
}
.toast {
width: var(--toastSize);
height: var(--toastSize);
position: fixed;
top: calc(50vh - var(--toastSize)/2);
left: calc(50vw - var(--toastSize)/2);
background: #4C4C4C;
border-radius: 12px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, .9);
}
.container {
width: var(--containerSize);
display: inherit;
flex-direction: inherit;
align-items: inherit;
margin: 0 auto 16px;
}
.container > svg {
height: var(--containerSize);
}
.loading {
animation: rotate linear 1s infinite;
}
@keyframes rotate {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
应用
路由
loading
的首要利用场景就是页面间的切换。
应用lazy()
和Suspense
配合React Router实现页面间切换时新页面加载loading
的显隐:
// App.jsx
import React, {Suspense, lazy} from 'react'
import {BrowserRouter, Routes, Route} from 'react-router-dom'
const Home = lazy(() => import('./routes/Home'))
const About = lazy(() => import('./routes/About'))
const App = () => (
<Router>
<Suspense fallback={<Toast/>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
);
不过,目前这个组合有一个致命毛病:
即新组件尚未实现加载时,旧组件曾经暗藏,且不论新组件加载得多快,Suspense
的fallback
都会执行,这将导致页面切换时呈现闪动。
尽管官网推出了startTransition()
解决闪动问题,但该计划目前尚未实用于路由。
所以这个组合实际中不举荐应用,因为以页面闪动为代价实现loading
得失相当。
过渡
React
新增的useTranstion() Hook
,搭配useState()
,实现动静组件加载时loading
的显隐。
import {useEffect, useState, useTransition} from 'react'
import {url} from '../../configuration'
import Toast from '../../../components/toast/Toast'
import Table from '../../../components/table/Table'
export default function User() {
const [res, setRes] = useState({})
const [loading, startTransition] = useTransition() // loading示意过渡工作的期待状态
useEffect(() => {
fetch(`${url}`, {method: 'GET'})
.then(r => r.json())
.then(d => {
if (d.hasOwnProperty('err'))
alert(`${d.text}:${d.err}`)
else if (
d.hasOwnProperty('data')
)
startTransition(() => {setRes(d)}) // 将setRes()标记为过渡工作
})
.catch(e => {alert(e)})
}, [])
return (
<div>
{loading && <Toast/>}
<Table
res={res}
/>
</div>
)
}
useTransition()
简直能笼罩loading
的绝大部分场景,因为在React
中,组件的更新根本都是通过在useEffect()
的依赖数组中增加state
来实现。
总结
本着官网反对的绝不本人再封装的准则,loading
的需要算是根本实现了,后续开发中若有新播种再来同步。
若有有余,欢送斧正。
发表回复