React 18.x 学习计划 - 第九天:React 18高级特性和最佳实践

学习目标

  • 理解React 18并发特性
  • 掌握useTransition和useDeferredValue
  • 学会使用Suspense改进
  • 理解自动批处理
  • 掌握React 18最佳实践

学习时间安排

总时长:8-9小时

  • React 18新特性概述:1小时
  • 并发特性深入:2.5小时
  • Suspense改进:1.5小时
  • 自动批处理:1小时
  • 最佳实践:1.5小时
  • 实践项目:2-3小时

第一部分:React 18新特性概述 (1小时)

1.1 React 18主要变化

版本升级说明(详细注释版)
javascript 复制代码
// React 18的主要变化包括:
// 1. 并发渲染(Concurrent Rendering)
// 2. 自动批处理(Automatic Batching)
// 3. Suspense改进
// 4. 新的Hooks:useTransition、useDeferredValue、useId、useSyncExternalStore
// 5. 服务端渲染改进

// 升级到React 18的步骤
// 1. 安装React 18
// npm install react@18 react-dom@18

// 2. 更新入口文件
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

// React 18使用createRoot API替代render
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// 3. 更新TypeScript类型(如果使用TypeScript)
// npm install --save-dev @types/react@18 @types/react-dom@18

1.2 并发渲染基础

并发渲染概念(详细注释版)
javascript 复制代码
// 并发渲染是React 18的核心特性
// 它允许React中断、暂停、恢复渲染工作
// 这使得React能够优先处理更重要的更新

// 传统渲染(同步)
// React会一次性完成所有更新,无法中断
// 如果更新耗时较长,会阻塞用户交互

// 并发渲染(异步)
// React可以将更新分解为多个小任务
// 可以在任务之间暂停,处理用户交互
// 然后继续完成剩余的更新

// 示例:传统渲染的问题
function TraditionalComponent() {
  const [count, setCount] = useState(0);
  const [items, setItems] = useState([]);

  // 这个更新会阻塞UI
  const handleUpdate = () => {
    setCount(count + 1);
    // 大量数据更新会阻塞
    setItems(generateLargeArray(10000));
  };

  return (
    <div>
      <button onClick={handleUpdate}>Update</button>
      <p>Count: {count}</p>
      <List items={items} />
    </div>
  );
}

// 示例:并发渲染的解决方案
function ConcurrentComponent() {
  const [count, setCount] = useState(0);
  const [items, setItems] = useState([]);
  const [isPending, startTransition] = useTransition();

  // 使用startTransition标记非紧急更新
  const handleUpdate = () => {
    setCount(count + 1);
    startTransition(() => {
      // 这个更新可以被中断
      setItems(generateLargeArray(10000));
    });
  };

  return (
    <div>
      <button onClick={handleUpdate} disabled={isPending}>
        {isPending ? 'Updating...' : 'Update'}
      </button>
      <p>Count: {count}</p>
      <List items={items} />
    </div>
  );
}

第二部分:并发特性深入 (2.5小时)

2.1 useTransition Hook

useTransition基础使用(详细注释版)
javascript 复制代码
// src/hooks/useTransitionExample.js
// 导入React和useTransition
import React, { useState, useTransition } from 'react';

// 定义使用useTransition的组件
function SearchResults({ query }) {
  // 使用useState Hook管理结果
  const [results, setResults] = useState([]);
  
  // 使用useTransition Hook
  // isPending表示是否有待处理的过渡更新
  // startTransition用于标记非紧急更新
  const [isPending, startTransition] = useTransition();

  // 搜索函数
  const performSearch = (searchQuery) => {
    // 使用startTransition包装非紧急更新
    startTransition(() => {
      // 这个更新可以被中断
      // 如果用户继续输入,React会中断这个更新
      const filteredResults = expensiveSearch(searchQuery);
      setResults(filteredResults);
    });
  };

  // 当查询变化时执行搜索
  React.useEffect(() => {
    if (query) {
      performSearch(query);
    }
  }, [query]);

  return (
    <div>
      {isPending && <div>Searching...</div>}
      <ul>
        {results.map(result => (
          <li key={result.id}>{result.name}</li>
        ))}
      </ul>
    </div>
  );
}

// 定义主搜索组件
function SearchApp() {
  const [query, setQuery] = useState('');

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
      />
      <SearchResults query={query} />
    </div>
  );
}

// 模拟昂贵的搜索操作
function expensiveSearch(query) {
  // 模拟大量数据处理
  const allItems = Array.from({ length: 10000 }, (_, i) => ({
    id: i,
    name: `Item ${i}`
  }));

  return allItems.filter(item =>
    item.name.toLowerCase().includes(query.toLowerCase())
  );
}

// 导出组件
export default SearchApp;
useTransition高级用法(详细注释版)
javascript 复制代码
// src/components/TransitionExample.js
// 导入React和useTransition
import React, { useState, useTransition, useDeferredValue } from 'react';

// 定义使用过渡的列表组件
function TransitionList({ items }) {
  // 使用useTransition Hook
  const [isPending, startTransition] = useTransition();
  
  // 使用useState Hook管理过滤后的项目
  const [filteredItems, setFilteredItems] = useState(items);

  // 处理过滤
  const handleFilter = (filterValue) => {
    // 使用startTransition标记非紧急更新
    startTransition(() => {
      const filtered = items.filter(item =>
        item.name.toLowerCase().includes(filterValue.toLowerCase())
      );
      setFilteredItems(filtered);
    });
  };

  return (
    <div>
      <input
        type="text"
        placeholder="Filter items..."
        onChange={(e) => handleFilter(e.target.value)}
      />
      
      {isPending && <div>Filtering...</div>}
      
      <ul>
        {filteredItems.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

// 定义使用多个过渡的组件
function MultipleTransitions() {
  const [count, setCount] = useState(0);
  const [items, setItems] = useState([]);
  const [isPending, startTransition] = useTransition();

  // 处理紧急更新
  const handleUrgentUpdate = () => {
    // 这个更新是紧急的,不使用startTransition
    setCount(count + 1);
  };

  // 处理非紧急更新
  const handleNonUrgentUpdate = () => {
    // 使用startTransition标记非紧急更新
    startTransition(() => {
      // 这个更新可以被中断
      const newItems = generateLargeArray(10000);
      setItems(newItems);
    });
  };

  return (
    <div>
      <div>
        <p>Count: {count}</p>
        <button onClick={handleUrgentUpdate}>Increment (Urgent)</button>
      </div>
      
      <div>
        <button onClick={handleNonUrgentUpdate} disabled={isPending}>
          {isPending ? 'Loading...' : 'Load Items (Non-Urgent)'}
        </button>
        <p>Items: {items.length}</p>
      </div>
    </div>
  );
}

// 生成大量数据
function generateLargeArray(count) {
  return Array.from({ length: count }, (_, i) => ({
    id: i,
    name: `Item ${i}`,
    value: Math.random() * 100
  }));
}

// 导出组件
export { TransitionList, MultipleTransitions };

2.2 useDeferredValue Hook

useDeferredValue基础使用(详细注释版)
javascript 复制代码
// src/components/DeferredValueExample.js
// 导入React和useDeferredValue
import React, { useState, useDeferredValue, useMemo } from 'react';

// 定义使用useDeferredValue的组件
function DeferredSearchResults({ query }) {
  // 使用useDeferredValue延迟更新值
  // 这个值会延迟更新,允许更紧急的更新先完成
  const deferredQuery = useDeferredValue(query);
  
  // 使用useMemo记忆化搜索结果
  // 只有当deferredQuery变化时才重新计算
  const results = useMemo(() => {
    if (!deferredQuery) return [];
    
    // 模拟昂贵的搜索操作
    return expensiveSearch(deferredQuery);
  }, [deferredQuery]);

  // 检查值是否过时
  const isStale = query !== deferredQuery;

  return (
    <div>
      {isStale && <div>Searching for "{query}"...</div>}
      <ul>
        {results.map(result => (
          <li key={result.id}>{result.name}</li>
        ))}
      </ul>
    </div>
  );
}

// 定义主搜索组件
function DeferredSearchApp() {
  const [query, setQuery] = useState('');

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
      />
      <DeferredSearchResults query={query} />
    </div>
  );
}

// 模拟昂贵的搜索操作
function expensiveSearch(query) {
  const allItems = Array.from({ length: 10000 }, (_, i) => ({
    id: i,
    name: `Item ${i}`
  }));

  return allItems.filter(item =>
    item.name.toLowerCase().includes(query.toLowerCase())
  );
}

// 定义使用useDeferredValue的列表组件
function DeferredList({ items }) {
  const [filter, setFilter] = useState('');
  
  // 延迟过滤值
  const deferredFilter = useDeferredValue(filter);
  
  // 使用useMemo记忆化过滤结果
  const filteredItems = useMemo(() => {
    if (!deferredFilter) return items;
    
    return items.filter(item =>
      item.name.toLowerCase().includes(deferredFilter.toLowerCase())
    );
  }, [items, deferredFilter]);

  // 检查值是否过时
  const isStale = filter !== deferredFilter;

  return (
    <div>
      <input
        type="text"
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        placeholder="Filter items..."
      />
      
      {isStale && <div>Filtering...</div>}
      
      <ul>
        {filteredItems.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

// 导出组件
export { DeferredSearchApp, DeferredList };

2.3 useId Hook

useId基础使用(详细注释版)
javascript 复制代码
// src/components/UseIdExample.js
// 导入React和useId
import React, { useId } from 'react';

// 定义使用useId的表单组件
function FormWithId() {
  // 使用useId生成唯一ID
  // 这个ID在服务端和客户端渲染时保持一致
  const nameId = useId();
  const emailId = useId();
  const passwordId = useId();

  return (
    <form>
      <div>
        <label htmlFor={nameId}>Name:</label>
        <input type="text" id={nameId} name="name" />
      </div>
      
      <div>
        <label htmlFor={emailId}>Email:</label>
        <input type="email" id={emailId} name="email" />
      </div>
      
      <div>
        <label htmlFor={passwordId}>Password:</label>
        <input type="password" id={passwordId} name="password" />
      </div>
    </form>
  );
}

// 定义使用useId的复选框组件
function CheckboxWithId({ label, checked, onChange }) {
  // 使用useId生成唯一ID
  const id = useId();

  return (
    <div>
      <input
        type="checkbox"
        id={id}
        checked={checked}
        onChange={onChange}
      />
      <label htmlFor={id}>{label}</label>
    </div>
  );
}

// 定义使用useId的多个输入组件
function MultipleInputs() {
  // 使用useId生成基础ID
  const baseId = useId();

  return (
    <div>
      <div>
        <label htmlFor={`${baseId}-first`}>First Name:</label>
        <input type="text" id={`${baseId}-first`} />
      </div>
      
      <div>
        <label htmlFor={`${baseId}-last`}>Last Name:</label>
        <input type="text" id={`${baseId}-last`} />
      </div>
    </div>
  );
}

// 导出组件
export { FormWithId, CheckboxWithId, MultipleInputs };

2.4 useSyncExternalStore Hook

useSyncExternalStore基础使用(详细注释版)
javascript 复制代码
// src/hooks/useSyncExternalStoreExample.js
// 导入React和useSyncExternalStore
import React, { useSyncExternalStore } from 'react';

// 定义外部存储类
class ExternalStore {
  constructor(initialState = {}) {
    this.state = initialState;
    this.listeners = new Set();
  }

  // 获取当前状态
  getSnapshot() {
    return this.state;
  }

  // 订阅状态变化
  subscribe(listener) {
    this.listeners.add(listener);
    
    // 返回取消订阅函数
    return () => {
      this.listeners.delete(listener);
    };
  }

  // 更新状态
  setState(newState) {
    this.state = { ...this.state, ...newState };
    
    // 通知所有订阅者
    this.listeners.forEach(listener => listener());
  }
}

// 创建全局存储实例
const globalStore = new ExternalStore({ count: 0, name: 'Initial' });

// 定义使用useSyncExternalStore的组件
function SyncExternalStoreComponent() {
  // 使用useSyncExternalStore Hook
  // 这个Hook用于订阅外部数据源
  const state = useSyncExternalStore(
    // 订阅函数
    (listener) => globalStore.subscribe(listener),
    // 获取快照函数
    () => globalStore.getSnapshot(),
    // 服务端快照函数(可选)
    () => globalStore.getSnapshot()
  );

  // 处理增加计数
  const handleIncrement = () => {
    globalStore.setState({ count: state.count + 1 });
  };

  // 处理更新名称
  const handleNameChange = (newName) => {
    globalStore.setState({ name: newName });
  };

  return (
    <div>
      <h2>External Store Component</h2>
      <p>Count: {state.count}</p>
      <p>Name: {state.name}</p>
      
      <button onClick={handleIncrement}>Increment</button>
      
      <input
        type="text"
        value={state.name}
        onChange={(e) => handleNameChange(e.target.value)}
        placeholder="Enter name"
      />
    </div>
  );
}

// 定义使用多个外部存储的组件
function MultipleStoresComponent() {
  // 创建多个存储实例
  const store1 = React.useMemo(() => new ExternalStore({ value: 0 }), []);
  const store2 = React.useMemo(() => new ExternalStore({ value: 0 }), []);

  // 订阅多个存储
  const state1 = useSyncExternalStore(
    (listener) => store1.subscribe(listener),
    () => store1.getSnapshot()
  );

  const state2 = useSyncExternalStore(
    (listener) => store2.subscribe(listener),
    () => store2.getSnapshot()
  );

  return (
    <div>
      <div>
        <p>Store 1: {state1.value}</p>
        <button onClick={() => store1.setState({ value: state1.value + 1 })}>
          Increment Store 1
        </button>
      </div>
      
      <div>
        <p>Store 2: {state2.value}</p>
        <button onClick={() => store2.setState({ value: state2.value + 1 })}>
          Increment Store 2
        </button>
      </div>
    </div>
  );
}

// 导出组件和存储类
export { SyncExternalStoreComponent, MultipleStoresComponent, ExternalStore };

第三部分:Suspense改进 (1.5小时)

3.1 Suspense基础

Suspense基础使用(详细注释版)
javascript 复制代码
// src/components/SuspenseExample.js
// 导入React和Suspense
import React, { Suspense, lazy } from 'react';

// 使用lazy函数动态导入组件
// 这些组件会被代码分割,只在需要时加载
const LazyComponent = lazy(() => import('./LazyComponent'));
const AnotherLazyComponent = lazy(() => import('./AnotherLazyComponent'));

// 定义加载中组件
function LoadingFallback() {
  return (
    <div className="loading-fallback">
      <div className="spinner"></div>
      <p>Loading component...</p>
    </div>
  );
}

// 定义使用Suspense的组件
function SuspenseApp() {
  const [showComponent, setShowComponent] = React.useState(false);

  return (
    <div>
      <button onClick={() => setShowComponent(!showComponent)}>
        {showComponent ? 'Hide' : 'Show'} Lazy Component
      </button>
      
      {/* 使用Suspense包装懒加载组件 */}
      {/* fallback属性指定加载时显示的组件 */}
      <Suspense fallback={<LoadingFallback />}>
        {showComponent && <LazyComponent />}
      </Suspense>
    </div>
  );
}

// 定义嵌套Suspense组件
function NestedSuspense() {
  return (
    <div>
      <Suspense fallback={<div>Loading main content...</div>}>
        <MainContent />
        
        <Suspense fallback={<div>Loading sidebar...</div>}>
          <Sidebar />
        </Suspense>
      </Suspense>
    </div>
  );
}

// 定义主内容组件
function MainContent() {
  return <div>Main Content</div>;
}

// 定义侧边栏组件
function Sidebar() {
  return <div>Sidebar</div>;
}

// 导出组件
export { SuspenseApp, NestedSuspense };

3.2 Suspense与数据获取

Suspense数据获取(详细注释版)
javascript 复制代码
// src/components/SuspenseDataFetching.js
// 导入React和Suspense
import React, { Suspense, useState, useTransition } from 'react';

// 定义数据获取函数
// 这个函数会抛出Promise,Suspense会捕获它
function fetchUserData(userId) {
  let status = 'pending';
  let result;
  
  const promise = fetch(`/api/users/${userId}`)
    .then(response => response.json())
    .then(data => {
      status = 'success';
      result = data;
    })
    .catch(error => {
      status = 'error';
      result = error;
    });
  
  return {
    read() {
      if (status === 'pending') {
        throw promise;
      } else if (status === 'error') {
        throw result;
      } else {
        return result;
      }
    }
  };
}

// 定义使用Suspense的用户组件
function UserProfile({ userId }) {
  // 读取用户数据
  // 如果数据还在加载,会抛出Promise,Suspense会捕获
  const userData = fetchUserData(userId).read();

  return (
    <div>
      <h2>{userData.name}</h2>
      <p>Email: {userData.email}</p>
      <p>Phone: {userData.phone}</p>
    </div>
  );
}

// 定义使用Suspense的主组件
function SuspenseDataApp() {
  const [userId, setUserId] = useState(1);
  const [isPending, startTransition] = useTransition();

  // 处理用户ID变化
  const handleUserIdChange = (newUserId) => {
    startTransition(() => {
      setUserId(newUserId);
    });
  };

  return (
    <div>
      <div>
        <button onClick={() => handleUserIdChange(1)}>User 1</button>
        <button onClick={() => handleUserIdChange(2)}>User 2</button>
        <button onClick={() => handleUserIdChange(3)}>User 3</button>
      </div>
      
      {isPending && <div>Loading user...</div>}
      
      {/* 使用Suspense包装数据获取组件 */}
      <Suspense fallback={<div>Loading user profile...</div>}>
        <UserProfile userId={userId} />
      </Suspense>
    </div>
  );
}

// 定义错误边界组件
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  componentDidCatch(error, errorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h2>Something went wrong</h2>
          <p>{this.state.error?.message}</p>
        </div>
      );
    }

    return this.props.children;
  }
}

// 定义带错误边界的Suspense组件
function SuspenseWithErrorBoundary() {
  return (
    <ErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <UserProfile userId={1} />
      </Suspense>
    </ErrorBoundary>
  );
}

// 导出组件
export { SuspenseDataApp, SuspenseWithErrorBoundary };

第四部分:自动批处理 (1小时)

4.1 自动批处理概念

自动批处理示例(详细注释版)
javascript 复制代码
// src/components/AutomaticBatching.js
// 导入React和useState
import React, { useState, useEffect } from 'react';

// 定义使用自动批处理的组件
function AutomaticBatchingExample() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);
  const [name, setName] = useState('');

  // 在React 18中,所有状态更新都会自动批处理
  // 无论它们是在事件处理函数、Promise、setTimeout等中
  const handleClick = () => {
    // 这些更新会被批处理,只会触发一次重新渲染
    setCount(count + 1);
    setFlag(!flag);
    setName('Updated');
    
    // 在React 18之前,这可能会触发多次重新渲染
    // 在React 18中,只会触发一次重新渲染
  };

  // 在Promise中的更新也会被批处理
  const handleAsyncUpdate = () => {
    fetch('/api/data')
      .then(() => {
        // 这些更新会被批处理
        setCount(count + 1);
        setFlag(!flag);
        setName('Async Updated');
      });
  };

  // 在setTimeout中的更新也会被批处理
  const handleTimeoutUpdate = () => {
    setTimeout(() => {
      // 这些更新会被批处理
      setCount(count + 1);
      setFlag(!flag);
      setName('Timeout Updated');
    }, 1000);
  };

  // 如果需要立即应用更新,可以使用flushSync
  const handleImmediateUpdate = () => {
    // 导入flushSync
    const { flushSync } = require('react-dom');
    
    // 立即应用这个更新
    flushSync(() => {
      setCount(count + 1);
    });
    
    // 这个更新会在下一个渲染周期应用
    setFlag(!flag);
  };

  return (
    <div>
      <h2>Automatic Batching Example</h2>
      <p>Count: {count}</p>
      <p>Flag: {flag ? 'true' : 'false'}</p>
      <p>Name: {name}</p>
      
      <button onClick={handleClick}>Batch Update</button>
      <button onClick={handleAsyncUpdate}>Async Batch Update</button>
      <button onClick={handleTimeoutUpdate}>Timeout Batch Update</button>
      <button onClick={handleImmediateUpdate}>Immediate Update</button>
    </div>
  );
}

// 定义使用useEffect的批处理示例
function BatchingWithEffect() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  // useEffect中的更新也会被批处理
  useEffect(() => {
    // 这些更新会被批处理
    setCount(1);
    setFlag(true);
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <p>Flag: {flag ? 'true' : 'false'}</p>
    </div>
  );
}

// 导出组件
export { AutomaticBatchingExample, BatchingWithEffect };

第五部分:最佳实践 (1.5小时)

5.1 组件设计最佳实践

组件设计模式(详细注释版)
javascript 复制代码
// src/components/BestPractices.js
// 导入React
import React, { memo, useMemo, useCallback } from 'react';

// 最佳实践1:单一职责原则
// 每个组件应该只负责一个功能
function UserCard({ user }) {
  return (
    <div className="user-card">
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </div>
  );
}

// 最佳实践2:使用memo优化组件
// 只有当props变化时才重新渲染
const OptimizedUserCard = memo(function OptimizedUserCard({ user, onEdit }) {
  return (
    <div className="user-card">
      <h3>{user.name}</h3>
      <p>{user.email}</p>
      <button onClick={() => onEdit(user.id)}>Edit</button>
    </div>
  );
});

// 最佳实践3:使用useMemo记忆化计算结果
function ExpensiveCalculation({ data }) {
  // 使用useMemo记忆化计算结果
  const result = useMemo(() => {
    // 昂贵的计算
    return data.reduce((sum, item) => sum + item.value, 0);
  }, [data]);

  return <div>Result: {result}</div>;
}

// 最佳实践4:使用useCallback记忆化函数
function ParentComponent({ items }) {
  // 使用useCallback记忆化函数
  const handleItemClick = useCallback((itemId) => {
    console.log('Item clicked:', itemId);
  }, []);

  return (
    <div>
      {items.map(item => (
        <ChildComponent
          key={item.id}
          item={item}
          onClick={handleItemClick}
        />
      ))}
    </div>
  );
}

// 最佳实践5:提取自定义Hook
function useUserData(userId) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchUser = async () => {
      try {
        setLoading(true);
        const response = await fetch(`/api/users/${userId}`);
        const data = await response.json();
        setUser(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchUser();
  }, [userId]);

  return { user, loading, error };
}

// 最佳实践6:使用组合而非继承
function Card({ children, title }) {
  return (
    <div className="card">
      {title && <h2>{title}</h2>}
      <div className="card-content">
        {children}
      </div>
    </div>
  );
}

function CardHeader({ children }) {
  return <div className="card-header">{children}</div>;
}

function CardBody({ children }) {
  return <div className="card-body">{children}</div>;
}

function CardFooter({ children }) {
  return <div className="card-footer">{children}</div>;
}

// 使用组合
function ComposedCard() {
  return (
    <Card title="User Profile">
      <CardHeader>
        <h3>Header</h3>
      </CardHeader>
      <CardBody>
        <p>Body content</p>
      </CardBody>
      <CardFooter>
        <button>Action</button>
      </CardFooter>
    </Card>
  );
}

// 导出组件
export {
  UserCard,
  OptimizedUserCard,
  ExpensiveCalculation,
  ParentComponent,
  useUserData,
  Card,
  CardHeader,
  CardBody,
  CardFooter,
  ComposedCard
};

5.2 状态管理最佳实践

状态管理模式(详细注释版)
javascript 复制代码
// src/hooks/useStateManagement.js
// 导入React和useState
import React, { useState, useReducer, useCallback } from 'react';

// 最佳实践1:使用useReducer管理复杂状态
const initialState = {
  items: [],
  filter: 'all',
  sortBy: 'name',
  loading: false,
  error: null
};

function todoReducer(state, action) {
  switch (action.type) {
    case 'ADD_ITEM':
      return {
        ...state,
        items: [...state.items, action.payload]
      };
    case 'REMOVE_ITEM':
      return {
        ...state,
        items: state.items.filter(item => item.id !== action.payload)
      };
    case 'SET_FILTER':
      return {
        ...state,
        filter: action.payload
      };
    case 'SET_SORT':
      return {
        ...state,
        sortBy: action.payload
      };
    case 'SET_LOADING':
      return {
        ...state,
        loading: action.payload
      };
    case 'SET_ERROR':
      return {
        ...state,
        error: action.payload
      };
    default:
      return state;
  }
}

function TodoApp() {
  const [state, dispatch] = useReducer(todoReducer, initialState);

  const addItem = useCallback((item) => {
    dispatch({ type: 'ADD_ITEM', payload: item });
  }, []);

  const removeItem = useCallback((id) => {
    dispatch({ type: 'REMOVE_ITEM', payload: id });
  }, []);

  const setFilter = useCallback((filter) => {
    dispatch({ type: 'SET_FILTER', payload: filter });
  }, []);

  return (
    <div>
      {/* 组件内容 */}
    </div>
  );
}

// 最佳实践2:状态提升
function ParentComponent() {
  const [sharedState, setSharedState] = useState('');

  return (
    <div>
      <ChildComponent1
        value={sharedState}
        onChange={setSharedState}
      />
      <ChildComponent2
        value={sharedState}
        onChange={setSharedState}
      />
    </div>
  );
}

// 最佳实践3:使用Context共享状态
const AppContext = React.createContext();

function AppProvider({ children }) {
  const [state, setState] = useState({});

  const value = {
    state,
    setState,
    updateState: (updates) => {
      setState(prev => ({ ...prev, ...updates }));
    }
  };

  return (
    <AppContext.Provider value={value}>
      {children}
    </AppContext.Provider>
  );
}

// 导出
export { TodoApp, ParentComponent, AppProvider, AppContext };

第六部分:实践项目(详细注释版)

项目:高性能搜索应用

主应用组件(详细注释版)
javascript 复制代码
// src/App.js
// 导入React和Suspense
import React, { Suspense, useState, useTransition } from 'react';
// 导入组件
import SearchInput from './components/SearchInput';
import SearchResults from './components/SearchResults';
import LoadingFallback from './components/LoadingFallback';
// 导入样式
import './App.css';

// 定义主应用组件
function App() {
  // 使用useState Hook管理搜索查询
  const [query, setQuery] = useState('');
  
  // 使用useTransition Hook管理过渡状态
  const [isPending, startTransition] = useTransition();

  // 处理搜索查询变化
  const handleQueryChange = (newQuery) => {
    // 使用startTransition标记非紧急更新
    startTransition(() => {
      setQuery(newQuery);
    });
  };

  return (
    <div className="App">
      <header className="App-header">
        <h1>High Performance Search App</h1>
      </header>
      
      <main className="App-main">
        {/* 搜索输入组件 */}
        <SearchInput
          value={query}
          onChange={handleQueryChange}
          isPending={isPending}
        />
        
        {/* 使用Suspense包装搜索结果 */}
        <Suspense fallback={<LoadingFallback />}>
          <SearchResults query={query} />
        </Suspense>
      </main>
    </div>
  );
}

// 导出App组件
export default App;
搜索输入组件(详细注释版)
javascript 复制代码
// src/components/SearchInput.js
// 导入React和useDeferredValue
import React, { useDeferredValue, useState, useEffect } from 'react';

// 定义SearchInput组件
function SearchInput({ value, onChange, isPending }) {
  // 使用useState Hook管理本地输入值
  const [localValue, setLocalValue] = useState(value);
  
  // 使用useDeferredValue延迟更新
  const deferredValue = useDeferredValue(localValue);
  
  // 当延迟值变化时,调用onChange
  useEffect(() => {
    if (deferredValue !== value) {
      onChange(deferredValue);
    }
  }, [deferredValue, onChange, value]);

  // 处理输入变化
  const handleChange = (e) => {
    setLocalValue(e.target.value);
  };

  // 检查值是否过时
  const isStale = localValue !== deferredValue;

  return (
    <div className="search-input-container">
      <input
        type="text"
        className="search-input"
        value={localValue}
        onChange={handleChange}
        placeholder="Search..."
      />
      
      {isPending && (
        <div className="search-loading">
          Searching...
        </div>
      )}
      
      {isStale && !isPending && (
        <div className="search-stale">
          Updating results...
        </div>
      )}
    </div>
  );
}

// 导出SearchInput组件
export default SearchInput;
搜索结果组件(详细注释版)
javascript 复制代码
// src/components/SearchResults.js
// 导入React和useMemo
import React, { useMemo, useTransition } from 'react';

// 定义搜索结果组件
function SearchResults({ query }) {
  // 使用useTransition Hook管理过渡状态
  const [isPending, startTransition] = useTransition();
  
  // 使用useState Hook管理结果
  const [results, setResults] = React.useState([]);

  // 使用useMemo记忆化搜索结果
  const filteredResults = useMemo(() => {
    if (!query.trim()) {
      return [];
    }
    
    // 模拟昂贵的搜索操作
    return performSearch(query);
  }, [query]);

  // 当过滤结果变化时,使用startTransition更新状态
  React.useEffect(() => {
    startTransition(() => {
      setResults(filteredResults);
    });
  }, [filteredResults]);

  if (!query.trim()) {
    return (
      <div className="search-results-empty">
        <p>Enter a search query to see results</p>
      </div>
    );
  }

  if (isPending) {
    return (
      <div className="search-results-loading">
        <p>Loading results...</p>
      </div>
    );
  }

  if (results.length === 0) {
    return (
      <div className="search-results-empty">
        <p>No results found for "{query}"</p>
      </div>
    );
  }

  return (
    <div className="search-results">
      <h2>Search Results ({results.length})</h2>
      <ul className="results-list">
        {results.map(result => (
          <li key={result.id} className="result-item">
            <h3>{result.title}</h3>
            <p>{result.description}</p>
            <span className="result-meta">
              {result.category} - {result.date}
            </span>
          </li>
        ))}
      </ul>
    </div>
  );
}

// 模拟搜索函数
function performSearch(query) {
  // 模拟大量数据
  const allItems = Array.from({ length: 10000 }, (_, i) => ({
    id: i,
    title: `Item ${i}`,
    description: `Description for item ${i}`,
    category: ['Category A', 'Category B', 'Category C'][i % 3],
    date: new Date(Date.now() - Math.random() * 10000000000).toISOString()
  }));

  // 执行搜索
  return allItems.filter(item =>
    item.title.toLowerCase().includes(query.toLowerCase()) ||
    item.description.toLowerCase().includes(query.toLowerCase())
  );
}

// 导出SearchResults组件
export default SearchResults;
加载回退组件(详细注释版)
javascript 复制代码
// src/components/LoadingFallback.js
// 导入React
import React from 'react';

// 定义LoadingFallback组件
function LoadingFallback() {
  return (
    <div className="loading-fallback">
      <div className="spinner"></div>
      <p>Loading search results...</p>
    </div>
  );
}

// 导出LoadingFallback组件
export default LoadingFallback;
样式文件(详细注释版)
css 复制代码
/* src/App.css */
/* 应用主容器样式 */
.App {
  min-height: 100vh;
  font-family: Arial, sans-serif;
  background-color: #f5f5f5;
}

/* 应用头部样式 */
.App-header {
  background-color: #333;
  color: white;
  padding: 2rem;
  text-align: center;
}

.App-header h1 {
  margin: 0;
  font-size: 2rem;
}

/* 主要内容区域 */
.App-main {
  max-width: 1200px;
  margin: 0 auto;
  padding: 2rem;
}

/* 搜索输入容器样式 */
.search-input-container {
  margin-bottom: 2rem;
  position: relative;
}

.search-input {
  width: 100%;
  padding: 1rem;
  font-size: 1.2rem;
  border: 2px solid #ddd;
  border-radius: 8px;
  box-sizing: border-box;
}

.search-input:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25);
}

.search-loading {
  position: absolute;
  top: 50%;
  right: 1rem;
  transform: translateY(-50%);
  color: #007bff;
  font-size: 0.9rem;
}

.search-stale {
  position: absolute;
  top: 50%;
  right: 1rem;
  transform: translateY(-50%);
  color: #6c757d;
  font-size: 0.9rem;
}

/* 搜索结果样式 */
.search-results {
  background-color: white;
  border-radius: 8px;
  padding: 2rem;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.search-results h2 {
  margin-top: 0;
  color: #333;
  font-size: 1.5rem;
}

.results-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.result-item {
  padding: 1.5rem;
  border-bottom: 1px solid #eee;
  transition: background-color 0.2s;
}

.result-item:hover {
  background-color: #f8f9fa;
}

.result-item:last-child {
  border-bottom: none;
}

.result-item h3 {
  margin: 0 0 0.5rem 0;
  color: #333;
  font-size: 1.2rem;
}

.result-item p {
  margin: 0 0 0.5rem 0;
  color: #666;
  line-height: 1.6;
}

.result-meta {
  font-size: 0.9rem;
  color: #999;
}

/* 空状态样式 */
.search-results-empty {
  text-align: center;
  padding: 4rem 2rem;
  color: #666;
}

.search-results-empty p {
  font-size: 1.2rem;
  margin: 0;
}

/* 加载状态样式 */
.search-results-loading {
  text-align: center;
  padding: 4rem 2rem;
  color: #666;
}

.search-results-loading p {
  font-size: 1.2rem;
  margin: 0;
}

/* 加载回退样式 */
.loading-fallback {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 4rem 2rem;
  min-height: 400px;
}

.spinner {
  width: 50px;
  height: 50px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #007bff;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 1rem;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.loading-fallback p {
  color: #666;
  font-size: 1.1rem;
  margin: 0;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .App-main {
    padding: 1rem;
  }
  
  .search-input {
    font-size: 1rem;
    padding: 0.75rem;
  }
  
  .search-results {
    padding: 1rem;
  }
  
  .result-item {
    padding: 1rem;
  }
}
相关推荐
诸神缄默不语2 小时前
用Vite创建Vue3前端项目
前端·vite·cue3
阿蒙Amon2 小时前
JavaScript学习笔记:5.函数
javascript·笔记·学习
旧梦吟2 小时前
脚本 生成图片水印
前端·数据库·算法·golang·html5
How_doyou_do2 小时前
模态框与DOM,及React和Vue中的优化
前端·vue.js·react.js
慧慧吖@2 小时前
React高级用法
javascript·react.js·ecmascript
大江东去浪淘尽千古风流人物2 小时前
【bug】bug记录学习,Win系统下爆栈的表现, chkstk.asm 实际是栈溢出
学习·bug
正经教主2 小时前
【Trae+AI】和Trae学习搭建App_2.2.1:第4章·安卓APP调用Express后端实战1:前端调用后端
人工智能·学习·express
前端不太难2 小时前
RN 的导航体系太混乱,如何选型和架构设计?
前端·react native·架构
正经教主2 小时前
【Trae+AI】和Trae学习搭建App_1.2:第2章·App开发环境配置
android·学习·android studio