React 中hooks之useTransition使用总结

目录

  1. 概述
  2. 基本用法
  3. 使用场景
  4. 最佳实践
  5. 注意事项

概述

什么是 useTransition?

useTransition 是 React 18 引入的新 Hook,用于标记非紧急的状态更新。它允许组件在状态转换期间保持响应,通过将某些更新标记为"过渡"来推迟它们的渲染。

主要特点

  • 保持 UI 响应性
  • 区分紧急和非紧急更新
  • 提供加载状态指示器
  • 不会阻塞用户交互

基本用法

1. 基本语法

typescript 复制代码
import { useTransition } from 'react';

function MyComponent() {
  const [isPending, startTransition] = useTransition();
  const [count, setCount] = useState(0);

  const handleClick = () => {
    startTransition(() => {
      setCount(c => c + 1);  // 这个更新被标记为过渡,当某次更新造成页面阻塞时,用户点击其他组件操作,此时会降低此次更新的优先级,不阻塞页面渲染先更新优先级高的操作,startTransition中只能写同步的代码,异步代码会打断低优先级,比如不能使用setTimeout
    });
  };

  return (
    <div>
      {isPending && <Spinner />}
      <button onClick={handleClick}>Increment</button>
      <p>Count: {count}</p>
    </div>
  );
}//isPending为true是表示当前渲染阻塞避免页面出现卡顿现象先显示loading状态组件,直到isPending为false时将展示渲染好的组件

2. 带加载状态的示例

typescript 复制代码
function SearchResults() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  const [isPending, startTransition] = useTransition();

  const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    // 立即更新输入值(紧急更新)
    setQuery(e.target.value);

    // 将搜索结果更新标记为过渡(非紧急更新)
    startTransition(() => {
      // 模拟搜索操作
      const searchResults = performSearch(e.target.value);
      setResults(searchResults);
    });
  };

  return (
    <div>
      <input value={query} onChange={handleSearch} />
      {isPending ? (
        <div>Loading...</div>
      ) : (
        <ul>
          {results.map(result => (
            <li key={result.id}>{result.title}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

使用场景

1. 大量数据渲染

typescript 复制代码
function DataGrid() {
  const [data, setData] = useState([]);
  const [isPending, startTransition] = useTransition();
  const [filter, setFilter] = useState('');

  const handleFilterChange = (newFilter: string) => {
    setFilter(newFilter);  // 立即更新过滤条件

    startTransition(() => {
      // 延迟大量数据的过滤和渲染
      const filteredData = processLargeDataSet(newFilter);
      setData(filteredData);
    });
  };

  return (
    <div>
      <input 
        value={filter} 
        onChange={e => handleFilterChange(e.target.value)} 
      />
      {isPending ? (
        <LoadingGrid />
      ) : (
        <VirtualizedGrid data={data} />
      )}
    </div>
  );
}

2. 路由切换

typescript 复制代码
function App() {
  const [isPending, startTransition] = useTransition();
  const [currentPage, setCurrentPage] = useState('home');

  const navigate = (page: string) => {
    // 立即更新导航状态
    startTransition(() => {
      setCurrentPage(page);
    });
  };

  return (
    <div>
      <Navigation onNavigate={navigate} />
      {isPending ? (
        <PageTransitionSpinner />
      ) : (
        <Page name={currentPage} />
      )}
    </div>
  );
}

3. 表单验证

typescript 复制代码
function ComplexForm() {
  const [formData, setFormData] = useState({});
  const [errors, setErrors] = useState({});
  const [isPending, startTransition] = useTransition();

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    
    // 立即更新表单值
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));

    // 将复杂的验证逻辑标记为过渡
    startTransition(() => {
      const validationErrors = validateFormField(name, value);
      setErrors(prev => ({
        ...prev,
        [name]: validationErrors
      }));
    });
  };

  return (
    <form>
      <input 
        name="email" 
        onChange={handleChange} 
        value={formData.email || ''} 
      />
      {isPending ? (
        <ValidatingIndicator />
      ) : (
        errors.email && <ErrorMessage error={errors.email} />
      )}
    </form>
  );
}

最佳实践

  1. 区分更新优先级
typescript 复制代码
function UserProfile() {
  const [isPending, startTransition] = useTransition();
  
  const handleInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    // 高优先级:直接更新输入值
    setInputValue(e.target.value);
    
    // 低优先级:更新预览
    startTransition(() => {
      setPreviewData(generatePreview(e.target.value));
    });
  };
}
  1. 合理使用 isPending
typescript 复制代码
function LoadingStates() {
  const [isPending, startTransition] = useTransition();
  
  return (
    <div>
      {/* 使用骨架屏而不是简单的加载指示器 */}
      {isPending ? (
        <Skeleton />
      ) : (
        <Content />
      )}
    </div>
  );
}

注意事项

  1. 不要在 transition 中包含紧急更新
typescript 复制代码
// ❌ 错误示例
startTransition(() => {
  setInputValue(e.target.value);  // 这应该是紧急更新
});

// ✅ 正确示例
setInputValue(e.target.value);    // 紧急更新
startTransition(() => {
  setSearchResults(search(e.target.value));  // 非紧急更新
});
  1. 避免不必要的 transition
typescript 复制代码
// ❌ 不需要 transition
startTransition(() => {
  setCount(count + 1);  // 简单的状态更新不需要 transition
});

// ✅ 适合使用 transition
startTransition(() => {
  setFilteredItems(items.filter(complexFilter));  // 复杂计算
});

总结

  1. useTransition 适用场景:

    • 大量数据处理
    • 复杂 UI 更新
    • 后台计算
    • 非阻塞渲染
  2. 主要优势:

    • 提升用户体验
    • 保持 UI 响应性
    • 优化渲染性能
    • 提供加载状态
  3. 使用建议:

    • 合理区分更新优先级
    • 适当处理加载状态
    • 避免过度使用
    • 配合其他性能优化手段
相关推荐
ComPDFKit15 分钟前
使用 React PDF 构建 React.js PDF 查看器的指南
前端·react.js·pdf
Dragon Wu26 分钟前
taro 小程序 CoverImage Image src无法显示图片的问题
javascript·小程序·前端框架·taro
明长歌27 分钟前
HTML页面渲染过程
前端·html
搏博1 小时前
Hbuilder X4.65新建vue3项目存在的问题以及解决办法
前端·javascript·vue.js·ecmascript
Maya动画技术1 小时前
ollama调用千问2.5-vl视频图片UI界面小程序分享
前端·python·计算机视觉·视觉检测
孩子 你要相信光1 小时前
vue3/vue2大屏适配
前端·javascript·vue.js
xihaowen2 小时前
Android Edge-to-Edge
android·前端·edge
娃哈哈哈哈呀3 小时前
Vue 3 动态 ref 的使用方式(表格)
前端·javascript·vue.js
小妖6665 小时前
el-breadcrumb 面包屑第一项后面怎么写没有分隔符
javascript·vue.js·elementui
2401_896008195 小时前
GCC 使用说明
前端·javascript·算法