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. 使用建议:

    • 合理区分更新优先级
    • 适当处理加载状态
    • 避免过度使用
    • 配合其他性能优化手段
相关推荐
傻小胖25 分钟前
React 中hooks之useDeferredValue用法总结
前端·javascript·react.js
圣道寺31 分钟前
审计文件标识作为水印打印在pdf页面边角
java·前端·python·pdf·学习方法
奇奇怪怪的土豆饼38 分钟前
Vue3轮播图左右联动
前端·javascript·vue.js·前端框架
汪子熙1 小时前
深入解析 npm ci 的运行原理及其在 Angular 项目中的应用
javascript·后端
星叔2 小时前
Python脚本搬运当前文件夹及其子文件夹中所有的.c格式的文件到当前新建的文件夹中
java·服务器·前端
GIS学姐嘉欣2 小时前
GIS开发及计算机就业主流技术岗
前端·学习·gis·webgis
小安同学iter2 小时前
Web开发 -前端部分-CSS-2
前端·javascript·css·正则表达式·css3·html5
kdayjj9663 小时前
深入解析Python的xmltodict库:简化XML数据处理的利器
前端·数据库·python
m0_748244833 小时前
Go-Gin Web 框架完整教程
前端·golang·gin
摇光933 小时前
[前端算法]排序算法
前端·算法·排序算法