M4RKYU.SYSEdition 2027
Skip to content
LOCZH/安大略 · 加拿大/▸logs · 3 ways to create engaging react loading screens with hooks 37b1待机OK/--:--:--EST
M4M4RK_YUportfolio
  • 创作创作
    创作Overview
    • 作品精选案例与项目记录
    • 游戏可玩原型与游戏开发日志
  • 影像影像
    影像Overview
    • 照片影像合集与视觉实验
    • 商店印刷品、海报和限量物件
  • 写作写作
    写作Overview
    • 博客长篇开发日志与现场笔记
    • 笔记短观察、链接与代码片段
  • 资源资源
    资源Overview
    • 工具38 款浏览器内开发工具
    • 链接每日使用的开发与设计书签
  • 关于关于
  • 联系联系
EN

同步 · dev.to / @markyu

React Loading Screens Are a State Machine Problem

A practical React loading screen guide using hooks, request states, error states, CSS animation, and why spinners alone are not enough.

发布日期
May 3 '24
·
阅读时长
2 min read
·
点赞
7
reactfrontendjavascriptux
在 dev.to 查看

本页目录

  • The State Shape I Prefer
  • A Practical Fetch Component
  • CSS Spinner Without a Library
  • When Lottie Makes Sense
  • My Rule for Loading UI
  • Final Thought

A loading screen is not a spinner problem.

It is a state problem.

The UI needs to know whether the request is idle, loading, successful, empty, or failed. If you only track loading: true/false, your component will eventually lie to the user.

The State Shape I Prefer

const [state, setState] = useState({
  status: "idle",
  data: null,
  error: null,
});

The useful statuses:

idle -> loading -> success
               \-> empty
               \-> error

This is already better than done.

A Practical Fetch Component

import { useEffect, useState } from "react";

export default function Posts() {
  const [state, setState] = useState({
    status: "idle",
    data: [],
    error: null,
  });

  useEffect(() => {
    let ignore = false;

    async function loadPosts() {
      setState({ status: "loading", data: [], error: null });

      try {
        const res = await fetch("https://jsonplaceholder.typicode.com/posts");
        if (!res.ok) throw new Error(`HTTP ${res.status}`);

        const data = await res.json();
        if (ignore) return;

        setState({
          status: data.length ? "success" : "empty",
          data,
          error: null,
        });
      } catch (error) {
        if (!ignore) {
          setState({ status: "error", data: [], error });
        }
      }
    }

    loadPosts();

    return () => {
      ignore = true;
    };
  }, []);

  if (state.status === "loading") return <LoadingScreen />;
  if (state.status === "error") return <ErrorMessage error={state.error} />;
  if (state.status === "empty") return <p>No posts yet.</p>;

  return (
    <ul>
      {state.data.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

The ignore flag avoids setting state after the component unmounts. It is a small detail, but it prevents a class of annoying bugs.

CSS Spinner Without a Library

function LoadingScreen() {
  return (
    <div className="loader" role="status" aria-live="polite">
      <span className="spinner" />
      <p>Loading posts...</p>
    </div>
  );
}
.loader {
  min-height: 180px;
  display: grid;
  place-items: center;
  gap: 12px;
}

.spinner {
  width: 36px;
  height: 36px;
  border: 4px solid #e5e7eb;
  border-top-color: #2563eb;
  border-radius: 50%;
  animation: spin 800ms linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation: none;
  }
}

I prefer this over adding a spinner dependency for a simple app.

When Lottie Makes Sense

Lottie is useful when loading is part of the product experience: onboarding, file upload, payment confirmation, or a long-running AI task.

I would not use it for every API fetch.

If the request usually takes 200ms, a dramatic animation feels slower, not better.

My Rule for Loading UI

Load timeUI
< 300msavoid spinner if possible
300ms - 2ssmall inline loader
2s - 10sskeleton or progress context
> 10sprogress, cancellation, or background job

For AI-agent and long-running 2026 workflows, the last row matters a lot. Users need to know whether the system is still thinking, stuck, or waiting on a tool.

Final Thought

A good loading screen tells the truth.

It should not just spin. It should represent the real state of the request and give the user enough confidence to wait or recover.

How do you handle long-running loading states in your React apps?

相关阅读

React 19 Micro-Interactions Without Layout Jank

A practical React 19 micro-interactions guide focused on motion boundaries, CSS transitions, optimistic UI, reduced motion, and performance.

react

CSS Heart Animation: Small Demo, Real Animation Lessons

A cleaner CSS heart animation tutorial focused on transform, pseudo-elements, keyframes, and the small mistakes that break simple UI animations.

css

Frontend Linear Data Structures Deep Dive: Arrays, Stacks, Queues, and Linked Lists

The Big Picture Before diving into stacks, queues, and linked lists, it helps to know...

computerscience

原文发布

本文首发于 dev.to,评论与点赞保留在原站。

在 dev.to 继续阅读
上一篇A* Search Finally Clicked When I Drew the GridA practical developer-friendly explanation of heuristic search, Greedy Best-First Search, and A* using a grid example instead of textbook language.
返回全部文章
下一篇Docker Containers: The Commands That Prove IsolationA practical Docker container guide focused on the commands that show image layers, process isolation, networking, volumes, and debugging.
返回档案
M4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYU
始于 2024
ZhenXiao Mark YuZhenXiao Mark Yu
联系

看到什么有意思的?和我聊聊。

这是一个作品集,不是服务 · 但每一条留言我都会看 — 如果哪里让你有所触动,或者只想打个招呼,欢迎写信过来。

开启对话
频道开放

随时打个招呼 · 2026

--:--:--EST加拿大 安大略
  • 邮件
  • GitHub
  • dev.to
  • 领英
  • 推特 / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat

订阅

偶尔收到一封简讯

来自 m4rkyu.com 的笔记与日志——简短、标注日期、没有杂音。随时可退订。

作品

线上发布、游戏作品与视觉档案。

  • 项目
  • 游戏
  • 档案
  • 日志

资源

每日好用的工具与个人收藏的链接库。

  • 搜索
  • 最新
  • 工具
  • 链接
  • 笔记
  • 主题
  • 商店
RSSJSON Feed

工作室

背景、联系方式以及合作渠道。

  • 关于
  • 联系
  • 更新日志
  • 技术说明
  • 简历筹备中

社交

在常去的平台上找到我。

  • GitHub
  • dev.to
  • 领英
  • 推特 / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat
  • 邮件
© 2026 ZhenXiao Mark Yumarkyu0615@gmail.com
  • 邮件
  • GitHub
  • dev.to
  • 领英
  • 推特 / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat
隐私条款由 Next.js 16 · React 19 · Tailwind 4 构建