需要
家喻户晓,利用如果少了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.jsximport 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
的需要算是根本实现了,后续开发中若有新播种再来同步。
若有有余,欢送斧正。