React Stately异步状态:Promise与异步操作管理

【免费下载链接】react-spectrum 一系列帮助您构建适应性强、可访问性好、健壮性高的用户体验的库和工具。 【免费下载链接】react-spectrum 项目地址: https://gitcode.com/GitHub_Trending/re/react-spectrum

在现代Web应用中,异步操作无处不在——从API数据获取到文件上传,从实时搜索到分页加载。React Stately作为Adobe React Spectrum生态系统的状态管理核心,提供了强大的异步状态管理能力,特别是通过useAsyncList Hook来优雅处理Promise和异步操作。

异步状态管理的核心挑战

在构建响应式用户界面时,异步操作管理面临几个关键挑战:

mermaid

useAsyncList:异步列表状态管理利器

useAsyncList是React Stately提供的专门用于管理异步加载列表数据的Hook,它内置了对Promise、加载状态、错误处理和取消操作的支持。

核心特性概览

特性 描述 优势
自动状态管理 自动处理loading、error、success状态 减少样板代码
Promise集成 原生支持async/await和Promise 与现代异步模式完美契合
取消机制 内置AbortController支持 避免竞态条件
分页支持 内置loadMore和cursor管理 简化无限滚动实现
错误处理 统一的错误状态管理 简化错误UI展示

基础使用示例

import { useAsyncList } from '@react-stately/data';

function UserList() {
  const list = useAsyncList({
    async load({ signal, cursor }) {
      const response = await fetch(`/api/users?cursor=${cursor}`, { signal });
      if (!response.ok) throw new Error('Failed to load users');
      
      const { users, nextCursor } = await response.json();
      
      return {
        items: users,
        cursor: nextCursor
      };
    }
  });

  return (
    <div>
      {list.isLoading && <div>Loading...</div>}
      {list.error && <div>Error: {list.error.message}</div>}
      
      <ul>
        {list.items.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
      
      {list.cursor && (
        <button onClick={list.loadMore} disabled={list.isLoading}>
          Load More
        </button>
      )}
    </div>
  );
}

Promise生命周期管理

React Stately的异步状态管理遵循清晰的Promise生命周期:

mermaid

高级异步模式

1. 带过滤的异步搜索
const userList = useAsyncList({
  async load({ signal, filterText }) {
    const params = new URLSearchParams();
    if (filterText) params.append('search', filterText);
    
    const response = await fetch(`/api/users?${params}`, { signal });
    const users = await response.json();
    
    return { items: users };
  }
});

// 实时搜索
function handleSearch(query) {
  userList.setFilterText(query);
}
2. 服务器端排序
const sortedList = useAsyncList({
  async load({ signal, sortDescriptor }) {
    const params = new URLSearchParams();
    if (sortDescriptor) {
      params.append('sortBy', sortDescriptor.column);
      params.append('sortDirection', sortDescriptor.direction);
    }
    
    const response = await fetch(`/api/data?${params}`, { signal });
    const data = await response.json();
    
    return { 
      items: data,
      sortDescriptor 
    };
  }
});

// 触发排序
function handleSort(column, direction) {
  sortedList.sort({ column, direction });
}

错误处理与重试机制

健壮的错误处理策略

const robustList = useAsyncList({
  async load({ signal }) {
    try {
      const response = await fetch('/api/data', { signal });
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      const data = await response.json();
      return { items: data };
      
    } catch (error) {
      if (error.name === 'AbortError') {
        console.log('Request was aborted');
        throw error; // 重新抛出让useAsyncList处理
      }
      
      // 自定义错误处理
      console.error('Load failed:', error);
      throw error; // 必须重新抛出以触发error状态
    }
  }
});

// 重试机制
function handleRetry() {
  if (robustList.error) {
    robustList.reload();
  }
}

错误状态管理矩阵

错误类型 处理策略 UI反馈
网络错误 自动重试机制 显示重试按钮
服务器错误 记录错误信息 显示错误详情
数据验证错误 数据清理和恢复 显示验证错误
取消操作 静默处理 无用户提示

性能优化最佳实践

1. 请求去重与缓存

const optimizedList = useAsyncList({
  async load({ signal, items, cursor }) {
    // 简单的内存缓存机制
    if (cursor === null && items.length > 0) {
      return { items }; // 返回缓存数据
    }
    
    const response = await fetch(`/api/data?cursor=${cursor}`, { signal });
    const newData = await response.json();
    
    return {
      items: cursor ? [...items, ...newData] : newData,
      cursor: newData.nextCursor
    };
  }
});

2. 分批加载与虚拟滚动

const paginatedList = useAsyncList({
  async load({ signal, cursor }) {
    const pageSize = 20;
    const response = await fetch(
      `/api/items?limit=${pageSize}&cursor=${cursor}`, 
      { signal }
    );
    
    const { items, nextCursor } = await response.json();
    
    return {
      items,
      cursor: nextCursor,
      // 可以添加元数据用于UI显示
      metadata: { totalCount: response.headers.get('X-Total-Count') }
    };
  }
});

实战:构建完整的异步数据表格

function AsyncDataTable() {
  const [selectedKeys, setSelectedKeys] = useState(new Set());
  
  const list = useAsyncList({
    async load({ signal, sortDescriptor, filterText }) {
      const params = new URLSearchParams();
      
      if (sortDescriptor) {
        params.append('sort', `${sortDescriptor.column}:${sortDescriptor.direction}`);
      }
      
      if (filterText) {
        params.append('q', filterText);
      }
      
      const response = await fetch(`/api/products?${params}`, { signal });
      
      if (!response.ok) {
        throw new Error(`Failed to load: ${response.status}`);
      }
      
      const products = await response.json();
      
      return {
        items: products,
        sortDescriptor,
        filterText
      };
    }
  });

  return (
    <div>
      {/* 搜索过滤 */}
      <input
        type="text"
        placeholder="Search products..."
        onChange={(e) => list.setFilterText(e.target.value)}
        disabled={list.isLoading}
      />
      
      {/* 状态指示器 */}
      {list.isLoading && <div className="loading">Loading products...</div>}
      {list.error && (
        <div className="error">
          Error: {list.error.message}
          <button onClick={list.reload}>Retry</button>
        </div>
      )}
      
      {/* 数据表格 */}
      <table>
        <thead>
          <tr>
            <th onClick={() => list.sort({ column: 'name', direction: 'ascending' })}>
              Name {list.sortDescriptor?.column === 'name' && (list.sortDescriptor.direction === 'ascending' ? '↑' : '↓')}
            </th>
            <th onClick={() => list.sort({ column: 'price', direction: 'ascending' })}>
              Price {list.sortDescriptor?.column === 'price' && (list.sortDescriptor.direction === 'ascending' ? '↑' : '↓')}
            </th>
            <th>Category</th>
          </tr>
        </thead>
        <tbody>
          {list.items.map(product => (
            <tr key={product.id}>
              <td>{product.name}</td>
              <td>${product.price}</td>
              <td>{product.category}</td>
            </tr>
          ))}
        </tbody>
      </table>
      
      {/* 分页控制 */}
      {list.cursor && (
        <button 
          onClick={list.loadMore} 
          disabled={list.isLoading}
        >
          {list.isLoading ? 'Loading...' : 'Load More'}
        </button>
      )}
    </div>
  );
}

测试策略与调试技巧

单元测试异步状态

// 使用Jest测试useAsyncList
import { renderHook, act } from '@testing-library/react-hooks';
import { useAsyncList } from '@react-stately/data';

describe('useAsyncList', () => {
  it('should handle successful async loading', async () => {
    const mockLoad = jest.fn().mockResolvedValue({
      items: [{ id: 1, name: 'Test' }]
    });
    
    const { result, waitForNextUpdate } = renderHook(() => 
      useAsyncList({ load: mockLoad })
    );
    
    expect(result.current.isLoading).toBe(true);
    
    await waitForNextUpdate();
    
    expect(result.current.isLoading).toBe(false);
    expect(result.current.items).toHaveLength(1);
    expect(result.current.items[0].name).toBe('Test');
  });
  
  it('should handle errors gracefully', async () => {
    const mockLoad = jest.fn().mockRejectedValue(new Error('Failed'));
    
    const { result, waitForNextUpdate } = renderHook(() => 
      useAsyncList({ load: mockLoad })
    );
    
    await waitForNextUpdate();
    
    expect(result.current.error).toBeDefined();
    expect(result.current.error.message).toBe('Failed');
  });
});

调试状态流转

mermaid

总结与最佳实践

React Stately的异步状态管理通过useAsyncList提供了企业级的解决方案:

  1. 状态自动化:自动处理loading、success、error状态流转
  2. Promise集成:完美支持async/await现代异步模式
  3. 取消机制:内置AbortController防止竞态条件
  4. 错误恢复:提供简单的重试机制
  5. 性能优化:支持分页、过滤、排序等复杂场景

关键决策点

场景 推荐方案 注意事项
简单数据获取 useAsyncList基础用法 处理基本加载状态
复杂分页 cursor+loadMore 注意内存管理
实时搜索 setFilterText 防抖优化性能
服务器排序 sort方法 保持UI状态同步
错误敏感场景 自定义错误处理 提供重试机制

通过掌握React Stately的异步状态管理,开发者可以构建出更加健壮、响应迅速的用户界面,有效处理现代Web应用中的各种异步操作场景。

【免费下载链接】react-spectrum 一系列帮助您构建适应性强、可访问性好、健壮性高的用户体验的库和工具。 【免费下载链接】react-spectrum 项目地址: https://gitcode.com/GitHub_Trending/re/react-spectrum

更多推荐