共计 2904 个字符,预计需要花费 8 分钟才能阅读完成。
需要
家喻户晓,利用如果少了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
的需要算是根本实现了,后续开发中若有新播种再来同步。
若有有余,欢送斧正。