React loading

需求

众所周知,应用如果少了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 (
        
{ type === 'success' ? : type === 'fail' ? : }
{ message ? message : type === 'success' ? '完成' : type === 'fail' ? '失败' : '加载中' }
) } 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 = () => (
    
        }>
            
                } />
                } />
            
        
    
);

不过,目前这个组合有一个致命缺点

即新组件尚未完成加载时,旧组件已经隐藏,且不论新组件加载得多快,Suspensefallback都会执行,这将导致页面切换时出现闪烁

虽然官方推出了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 (
        
{loading && } ) }

useTransition()几乎能覆盖loading的绝大部分场景,因为在React中,组件的更新基本都是通过在useEffect()的依赖数组中添加state来实现。

总结

本着官方支持的绝不自己再封装的原则,loading的需求算是基本实现了,后续开发中若有新收获再来同步。

若有不足,欢迎指正。

你可能感兴趣的:(React loading)