React用户界面模式Skill react-ui-patterns

这个技能提供React应用中处理加载状态、错误处理和异步数据获取的现代UI模式。它适用于构建UI组件、管理异步数据或处理UI状态,包括骨架屏、错误显示、按钮状态和表单提交等模式,旨在提升用户体验和应用性能。关键词:React, UI模式, 加载状态, 错误处理, 数据获取, 前端开发, 用户界面, 用户体验, 骨架屏, 乐观更新。

前端开发 0 次安装 0 次浏览 更新于 3/21/2026

name: react-ui-patterns description: 用于加载状态、错误处理和异步数据获取的现代React UI模式。在构建UI组件、处理异步数据或管理UI状态时使用。

React UI模式

核心原则

  1. 从不显示过时的UI - 仅在真正加载时显示加载指示器
  2. 始终暴露错误 - 用户必须知道何时发生故障
  3. 乐观更新 - 使UI感觉即时
  4. 渐进披露 - 内容可用时逐步显示
  5. 优雅降级 - 部分数据比没有数据更好

加载状态模式

黄金法则

仅在无数据显示时显示加载指示器。

// 正确 - 仅在没有数据时显示加载
const { data, loading, error } = useGetItemsQuery();

if (error) return <ErrorState error={error} onRetry={refetch} />;
if (loading && !data) return <LoadingState />;
if (!data?.items.length) return <EmptyState />;

return <ItemList items={data.items} />;
// 错误 - 即使有缓存数据也显示微调器
if (loading) return <LoadingState />; // 重新获取时闪烁!

加载状态决策树

是否有错误?
  → 是:显示错误状态,并提供重试选项
  → 否:继续

是否正在加载且没有数据?
  → 是:显示加载指示器(微调器/骨架屏)
  → 否:继续

是否有数据?
  → 是,有项目:显示数据
  → 是,但为空:显示空状态
  → 否:显示加载(备用)

骨架屏 vs 微调器

使用骨架屏当 使用微调器当
内容形状已知 内容形状未知
列表/卡片布局 模态操作
初始页面加载 按钮提交
内容占位符 内联操作

错误处理模式

错误处理层次结构

1. 内联错误(字段级) → 表单验证错误
2. 吐司通知 → 可恢复错误,用户可以重试
3. 错误横幅 → 页面级错误,数据仍部分可用
4. 完整错误屏幕 → 不可恢复,需要用户操作

始终显示错误

关键:永远不要默默吞掉错误。

// 正确 - 错误始终暴露给用户
const [createItem, { loading }] = useCreateItemMutation({
  onCompleted: () => {
    toast.success({ title: '项目已创建' });
  },
  onError: (error) => {
    console.error('createItem失败:', error);
    toast.error({ title: '创建项目失败' });
  },
});

// 错误 - 错误被静默捕获,用户一无所知
const [createItem] = useCreateItemMutation({
  onError: (error) => {
    console.error(error); // 用户看不到任何东西!
  },
});

错误状态组件模式

interface ErrorStateProps {
  error: Error;
  onRetry?: () => void;
  title?: string;
}

const ErrorState = ({ error, onRetry, title }: ErrorStateProps) => (
  <div className="error-state">
    <Icon name="exclamation-circle" />
    <h3>{title ?? '出了点问题'}</h3>
    <p>{error.message}</p>
    {onRetry && (
      <Button onClick={onRetry}>重试</Button>
    )}
  </div>
);

按钮状态模式

按钮加载状态

<Button
  onClick={handleSubmit}
  isLoading={isSubmitting}
  disabled={!isValid || isSubmitting}
>
  提交
</Button>

在操作期间禁用

关键:在异步操作期间始终禁用触发器。

// 正确 - 按钮在加载期间禁用
<Button
  disabled={isSubmitting}
  isLoading={isSubmitting}
  onClick={handleSubmit}
>
  提交
</Button>

// 错误 - 用户可以多次点击
<Button onClick={handleSubmit}>
  {isSubmitting ? '提交中...' : '提交'}
</Button>

空状态

空状态要求

每个列表/集合必须有空状态:

// 错误 - 没有空状态
return <FlatList data={items} />;

// 正确 - 显式空状态
return (
  <FlatList
    data={items}
    ListEmptyComponent={<EmptyState />}
  />
);

上下文空状态

// 搜索无结果
<EmptyState
  icon="search"
  title="未找到结果"
  description="尝试不同的搜索词"
/>

// 列表暂无项目
<EmptyState
  icon="plus-circle"
  title="暂无项目"
  description="创建您的第一个项目"
  action={{ label: '创建项目', onClick: handleCreate }}
/>

表单提交模式

const MyForm = () => {
  const [submit, { loading }] = useSubmitMutation({
    onCompleted: handleSuccess,
    onError: handleError,
  });

  const handleSubmit = async () => {
    if (!isValid) {
      toast.error({ title: '请修正错误' });
      return;
    }
    await submit({ variables: { input: values } });
  };

  return (
    <form>
      <Input
        value={values.name}
        onChange={handleChange('name')}
        error={touched.name ? errors.name : undefined}
      />
      <Button
        type="submit"
        onClick={handleSubmit}
        disabled={!isValid || loading}
        isLoading={loading}
      >
        提交
      </Button>
    </form>
  );
};

反模式

加载状态

// 错误 - 数据存在时显示微调器(导致闪烁)
if (loading) return <Spinner />;

// 正确 - 仅在没有数据时显示加载
if (loading && !data) return <Spinner />;

错误处理

// 错误 - 错误被吞掉
try {
  await mutation();
} catch (e) {
  console.log(e); // 用户一无所知!
}

// 正确 - 错误暴露
onError: (error) => {
  console.error('操作失败:', error);
  toast.error({ title: '操作失败' });
}

按钮状态

// 错误 - 按钮在提交期间未禁用
<Button onClick={submit}>提交</Button>

// 正确 - 禁用并显示加载
<Button onClick={submit} disabled={loading} isLoading={loading}>
  提交
</Button>

检查清单

在完成任何UI组件之前:

UI状态:

  • [ ] 错误状态已处理并向用户显示
  • [ ] 仅在无数据时显示加载状态
  • [ ] 为空集合提供空状态
  • [ ] 按钮在异步操作期间禁用
  • [ ] 按钮在适当显示加载指示器

数据与突变:

  • [ ] 突变有onError处理器
  • [ ] 所有用户操作都有反馈(吐司/视觉)

与其他技能的集成

  • graphql-schema:使用适当的错误处理模式进行突变
  • testing-patterns:测试所有UI状态(加载、错误、空、成功)
  • formik-patterns:应用表单提交模式