React 16 到 React 19 基础语法、API 对比与最佳实践

522 阅读13分钟

大家好,我是鱼樱!!!

关注公众号【鱼樱AI实验室】持续每天分享更多前端和AI辅助前端编码新知识~~喜欢的就一起学反正开源至上,无所谓被诋毁被喷被质疑文章没有价值~

一个城市淘汰的自由职业-农村前端程序员(虽然不靠代码挣钱,写文章就是为爱发电),兼职远程上班目前!!!热心坚持分享~~

1. 组件定义方式演变

1.1 类组件与函数组件

React 16:

// 类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// 函数组件(无状态)
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

React 16.8+:

// 函数组件(有状态)
function Welcome(props) {
  const [count, setCount] = useState(0);
  return <h1>Hello, {props.name}, Count: {count}</h1>;
}

最佳实践:

  • React 16-19: 优先使用函数组件和 Hooks,类组件仅用于维护旧代码
  • React 18+: 利用并发特性时,建议使用函数组件,类组件不支持某些新功能

2. 生命周期详细对比

2.1 类组件生命周期变化

React 16.0-16.2:

class Component extends React.Component {
  constructor(props) {
    super(props);
    // 初始化状态
  }
  
  componentWillMount() {
    // 组件挂载前 (已弃用)
  }
  
  componentDidMount() {
    // 组件挂载后
  }
  
  componentWillReceiveProps(nextProps) {
    // 组件接收新props前 (已弃用)
  }
  
  shouldComponentUpdate(nextProps, nextState) {
    // 控制是否重新渲染
    return true;
  }
  
  componentWillUpdate(nextProps, nextState) {
    // 组件更新前 (已弃用)
  }
  
  componentDidUpdate(prevProps, prevState) {
    // 组件更新后
  }
  
  componentWillUnmount() {
    // 组件卸载前
  }
}

React 16.3:

class Component extends React.Component {
  // 新增
  static getDerivedStateFromProps(props, state) {
    // 在render前调用,用于根据props更新state
    // 替代componentWillReceiveProps
    return null; // 返回null表示不更新状态
  }
  
  // 新增
  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 在DOM更新前调用,返回值会传递给componentDidUpdate
    // 替代componentWillUpdate
    return null;
  }
  
  componentDidUpdate(prevProps, prevState, snapshot) {
    // snapshot是getSnapshotBeforeUpdate的返回值
  }
}

React 16.3-16.8 弃用的生命周期:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

这些方法被标记为 UNSAFE_ 前缀版本:

  • UNSAFE_componentWillMount
  • UNSAFE_componentWillReceiveProps
  • UNSAFE_componentWillUpdate

React 17+ 完整生命周期:

class Component extends React.Component {
  constructor(props)
  
  static getDerivedStateFromProps(props, state)
  
  shouldComponentUpdate(nextProps, nextState)
  
  render()
  
  getSnapshotBeforeUpdate(prevProps, prevState)
  
  componentDidMount()
  
  componentDidUpdate(prevProps, prevState, snapshot)
  
  componentWillUnmount()
  
  // 错误处理
  static getDerivedStateFromError(error)
  
  componentDidCatch(error, info)
}

2.2 函数组件生命周期(Hooks)

React 16.8+ Hooks:

function Component(props) {
  // 相当于constructor和componentDidMount, componentDidUpdate, componentWillUnmount的组合
  useEffect(() => {
    // 相当于componentDidMount和componentDidUpdate
    console.log('组件挂载或更新后');
    
    return () => {
      // 相当于componentWillUnmount
      console.log('组件卸载前');
    };
  }, [/* 依赖数组 */]);
  
  // 仅在挂载时执行一次
  useEffect(() => {
    console.log('仅在组件挂载后');
    return () => {
      console.log('组件卸载前清理');
    };
  }, []);
  
  // 每次渲染后都执行
  useEffect(() => {
    console.log('每次渲染后执行');
  });
  
  // 根据特定依赖更新
  useEffect(() => {
    console.log('props.id变化时执行');
  }, [props.id]);
  
  // 相当于componentDidMount, componentDidUpdate和getSnapshotBeforeUpdate
  useLayoutEffect(() => {
    // DOM更新后、浏览器绘制前同步执行
  }, []);
}

React 18+ 新添加的 Hook:

function Component() {
  // 仅服务端渲染期间执行一次
  useInsertionEffect(() => {
    // 用于CSS-in-JS库注入样式
    // 在DOM创建后,useLayoutEffect前执行
  }, []);
  
  // 延迟执行,不阻塞渲染
  const deferredValue = useDeferredValue(value);
  
  // 过渡更新,标记非紧急更新
  const [isPending, startTransition] = useTransition();
  
  // React 18.2+,组件ID
  const id = useId();
  
  // React 18+,Suspense缓存
  useCacheRefresh();
}

React 19+ Hooks:

function Component() {
  // React 19 新增action hook管理表单状态
  const formAction = useFormAction({
    // 表单状态管理
  });
  
  // 新的effect优化API,减少重渲染
  useEffectEvent(() => {
    // 执行不需要重新渲染的逻辑
  });
}

2.3 类组件与函数组件生命周期对比表

类组件函数组件(Hooks)执行时机
constructoruseState, useRef初始化组件创建时
getDerivedStateFromProps在渲染过程中通过计算获取render前
shouldComponentUpdateReact.memo, useMemo决定是否重新渲染
render函数本体渲染组件
getSnapshotBeforeUpdateuseLayoutEffect(有限接近)DOM更新前
componentDidMountuseEffect([], ...)组件挂载后
componentDidUpdateuseEffect([deps], ...)组件更新后
componentWillUnmountuseEffect返回的函数组件卸载前
getDerivedStateFromError无直接对应,需使用错误边界子组件出错时
componentDidCatch无直接对应,需使用错误边界捕获子组件错误后

3. 状态管理演变

3.1 类组件状态管理

React 16:

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  
  increment = () => {
    this.setState({ count: this.state.count + 1 });
    // 或使用函数式更新
    this.setState(state => ({ count: state.count + 1 }));
  }
  
  render() {
    return (
      <button onClick={this.increment}>
        Count: {this.state.count}
      </button>
    );
  }
}

3.2 函数组件状态管理

React 16.8+:

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

React 18: 自动批处理更新 - 多个状态更新合并为一次渲染

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);
  
  function handleClick() {
    // React 18中这些会被自动批处理为一次更新
    setCount(c => c + 1); // 不会触发重新渲染
    setFlag(f => !f);     // 只有这后会触发一次重新渲染
  }
  
  // ...
}

React 19: 引入更细粒度的状态管理,useActionState帮助管理表单状态

function Form() {
  const [state, action] = useActionState({
    initialState: { name: "" },
    action: (state, newValue) => {
      return { ...state, ...newValue }
    }
  });
  
  return <form action={action}>
    <input name="name" defaultValue={state.name} />
  </form>;
}

3.3 状态管理最佳实践

  • React 16-19:
    • 使用函数式更新处理依赖前一状态的更新
    • 对象状态更新时保持不可变性
  • React 18+:
    • 利用自动批处理优化性能
    • 使用 useDeferredValue 和 useTransition 处理大型状态更新
  • React 19+:
    • 使用新的表单状态管理API简化表单处理

4. Props 处理与组件通信

4.1 Props 默认值

类组件:

class Button extends React.Component {
  static defaultProps = {
    color: 'blue'
  };
  
  render() {
    return <button className={this.props.color}>{this.props.children}</button>;
  }
}

函数组件:

function Button({ color = 'blue', children }) {
  return <button className={color}>{children}</button>;
}

// 或者
Button.defaultProps = {
  color: 'blue'
};

4.2 Props 类型检查

React 16-19:

import PropTypes from 'prop-types';

function Button({ color, children }) {
  return <button className={color}>{children}</button>;
}

Button.propTypes = {
  color: PropTypes.string,
  children: PropTypes.node.isRequired
};

TypeScript (推荐):

interface ButtonProps {
  color?: string;
  children: React.ReactNode;
}

function Button({ color = 'blue', children }: ButtonProps) {
  return <button className={color}>{children}</button>;
}

4.3 Context API 演变

React 16.0:

// 创建Context
const ThemeContext = React.createContext('light');

// 提供Context
class App extends React.Component {
  render() {
    return (
      <ThemeContext.Provider value="dark">
        <ThemedButton />
      </ThemeContext.Provider>
    );
  }
}

// 消费Context
class ThemedButton extends React.Component {
  static contextType = ThemeContext;
  
  render() {
    return <button theme={this.context}>Themed Button</button>;
  }
}

// 函数组件使用Consumer
function ThemedButton() {
  return (
    <ThemeContext.Consumer>
      {value => <button theme={value}>Themed Button</button>}
    </ThemeContext.Consumer>
  );
}

React 16.8+ (Hooks):

const ThemeContext = React.createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton />
    </ThemeContext.Provider>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button theme={theme}>Themed Button</button>;
}

5. 渲染优化各版本对比

5.1 类组件优化

React 16:

class ExpensiveComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // 手动控制重新渲染
    return this.props.value !== nextProps.value;
  }
  
  render() {
    return <div>{this.props.value}</div>;
  }
}

// 或使用PureComponent
class OptimizedComponent extends React.PureComponent {
  // 自动浅比较props和state
  render() {
    return <div>{this.props.value}</div>;
  }
}

5.2 函数组件优化

React 16.8+:

// React.memo - 类似PureComponent的函数组件版本
const MemoizedComponent = React.memo(function MyComponent(props) {
  return <div>{props.value}</div>;
});

// 自定义比较函数
const MemoizedCustomComponent = React.memo(
  function MyComponent(props) {
    return <div>{props.value}</div>;
  },
  (prevProps, nextProps) => {
    // 返回true表示不重新渲染
    return prevProps.value === nextProps.value;
  }
);

function App() {
  // useMemo - 缓存计算结果
  const expensiveValue = useMemo(() => {
    return computeExpensiveValue(a, b);
  }, [a, b]);
  
  // useCallback - 缓存函数引用
  const memoizedCallback = useCallback(() => {
    doSomething(a, b);
  }, [a, b]);
}

React 18+:

function SearchResults() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  
  // 输入时立即更新query,但延迟更新结果,提升用户体验
  const deferredQuery = useDeferredValue(query);
  
  useEffect(() => {
    fetchResults(deferredQuery).then(setResults);
  }, [deferredQuery]);
  
  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      {/* 可能很慢的渲染 */}
      <ResultsList query={deferredQuery} results={results} />
    </>
  );
}

React 19+:

// React 19中的自动记忆
function ResultItem({item}) {
  return <div className="item">{item.title}</div>;
}

// React会自动优化类似上述纯渲染组件
function ResultsList({items}) {
  return (
    <div>
      {items.map(item => <ResultItem key={item.id} item={item} />)}
    </div>
  );
}

6. 副作用处理

6.1 类组件

React 16:

class DataFetcher extends React.Component {
  state = { data: null, loading: true };
  
  componentDidMount() {
    this.fetchData();
  }
  
  componentDidUpdate(prevProps) {
    if (prevProps.id !== this.props.id) {
      this.fetchData();
    }
  }
  
  componentWillUnmount() {
    // 清理工作
    this.isUnmounted = true;
  }
  
  fetchData = async () => {
    this.setState({ loading: true });
    const data = await fetchAPI(this.props.id);
    
    // 防止组件卸载后设置状态
    if (!this.isUnmounted) {
      this.setState({ data, loading: false });
    }
  }
  
  render() {
    // ...
  }
}

6.2 函数组件

React 16.8+:

function DataFetcher({ id }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    let isMounted = true;
    setLoading(true);
    
    fetchAPI(id).then(data => {
      if (isMounted) {
        setData(data);
        setLoading(false);
      }
    });
    
    return () => {
      isMounted = false; // 清理函数
    };
  }, [id]); // 依赖数组,id变化时重新执行
  
  // ...
}

React 18+: 使用useTransition处理副作用

function SearchResults() {
  const [isPending, startTransition] = useTransition();
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  
  function handleChange(e) {
    // 立即更新输入值
    setQuery(e.target.value);
    
    // 标记结果更新为非紧急
    startTransition(async () => {
      const results = await fetchResults(e.target.value);
      setResults(results);
    });
  }
  
  return (
    <>
      <input value={query} onChange={handleChange} />
      {isPending ? (
        <div>Loading...</div>
      ) : (
        <ResultsList results={results} />
      )}
    </>
  );
}

7. 错误处理

7.1 错误边界

React 16+:

class ErrorBoundary extends React.Component {
  state = { hasError: false, error: null };
  
  static getDerivedStateFromError(error) {
    // 更新状态,下次渲染显示备用UI
    return { hasError: true, error };
  }
  
  componentDidCatch(error, info) {
    // 记录错误信息
    logErrorToService(error, info);
  }
  
  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    
    return this.props.children;
  }
}

// 使用
<ErrorBoundary>
  <MyComponent />
</ErrorBoundary>

注意: 截至React 19,仍然没有函数组件版本的错误边界,必须使用类组件。

7.2 React 18+ 新错误处理能力

// React 18新增全局API
function App() {
  return (
    <React.StrictMode>
      <ErrorBoundary
        fallback={<p>Application Error</p>}
        onError={(error, errorInfo) => {
          // 记录到服务
          logError(error, errorInfo);
        }}
      >
        <AppContent />
      </ErrorBoundary>
    </React.StrictMode>
  );
}

8. React 16 到 19 的重要 API 变更

8.1 新增API

API版本描述
React.lazy16.6组件代码分割
React.Suspense16.6等待加载组件
React.memo16.6函数组件性能优化
Hooks系列API16.8函数组件状态和生命周期
React.createRoot18.0新并发渲染器入口
useTransition18.0标记非紧急更新
useDeferredValue18.0延迟低优先级更新
useId18.0生成唯一ID
useInsertionEffect18.0CSS-in-JS库专用Hook
useFormState19.0表单状态管理
useFormStatus19.0表单提交状态
useEffectEvent19.0分离事件逻辑和渲染依赖
useOptimistic19.0乐观UI更新

8.2 弃用API

API弃用版本替代方案
componentWillMount16.3componentDidMount
componentWillReceiveProps16.3getDerivedStateFromProps
componentWillUpdate16.3getSnapshotBeforeUpdate
ReactDOM.render18.0ReactDOM.createRoot().render()
ReactDOM.hydrate18.0ReactDOM.hydrateRoot()
findDOMNode17.0Refs

8.3 重大变更

React 17:

  • 事件委托变更 - 从document改为挂载React树的根DOM
  • 移除事件池
  • useEffect清理函数变为异步
  • 一致的错误边界行为

React 18:

  • 自动批处理state更新
  • 并发渲染
  • 服务器组件
  • Suspense支持服务端渲染
  • transitions用于区分紧急和非紧急更新

React 19:

  • 更好的表单API
  • 自动化记忆优化
  • 服务器Actions
  • 资产加载优化
  • 错误边界改进

9. 代码组织最佳实践

9.1 组件设计原则

单一职责:

// 不推荐
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [posts, setPosts] = useState([]);
  
  useEffect(() => {
    fetchUser(userId).then(setUser);
    fetchPosts(userId).then(setPosts);
  }, [userId]);
  
  // 渲染用户信息和帖子列表...
}

// 推荐 - 分离关注点
function UserProfile({ userId }) {
  return (
    <div>
      <UserInfo userId={userId} />
      <UserPosts userId={userId} />
    </div>
  );
}

function UserInfo({ userId }) {
  const [user, setUser] = useState(null);
  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, [userId]);
  // 渲染用户信息...
}

function UserPosts({ userId }) {
  const [posts, setPosts] = useState([]);
  useEffect(() => {
    fetchPosts(userId).then(setPosts);
  }, [userId]);
  // 渲染帖子列表...
}

9.2 自定义Hook封装

// 封装数据获取逻辑
function useFetch(url, options) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    let isMounted = true;
    setLoading(true);
    
    fetch(url, options)
      .then(res => res.json())
      .then(data => {
        if (isMounted) {
          setData(data);
          setError(null);
          setLoading(false);
        }
      })
      .catch(err => {
        if (isMounted) {
          setError(err);
          setData(null);
          setLoading(false);
        }
      });
      
    return () => {
      isMounted = false;
    };
  }, [url, JSON.stringify(options)]);
  
  return { data, loading, error };
}

// 使用
function UserProfile({ userId }) {
  const { data: user, loading, error } = useFetch(`/api/users/${userId}`);
  
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error loading user</p>;
  
  return <div>{user.name}</div>;
}

9.3 React 18+ 并发特性最佳实践

function SearchPage() {
  const [searchTerm, setSearchTerm] = useState('');
  const [isPending, startTransition] = useTransition();
  
  function handleSearch(e) {
    // 立即更新输入值
    setSearchTerm(e.target.value);
    
    // 非紧急更新 - 搜索结果计算
    startTransition(() => {
      // 重量级操作...
    });
  }
  
  return (
    <>
      <input value={searchTerm} onChange={handleSearch} />
      {isPending ? <Spinner /> : <SearchResults term={searchTerm} />}
    </>
  );
}

9.4 React 19 表单处理最佳实践

'use client';

import { useFormState, useFormStatus } from 'react';

// 服务端Action
async function signup(prevState, formData) {
  try {
    const user = await createUser(formData);
    return { success: true, user };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

function SubmitButton() {
  const { pending } = useFormStatus();
  
  return (
    <button disabled={pending}>
      {pending ? 'Signing up...' : 'Sign up'}
    </button>
  );
}

function SignupForm() {
  const [state, formAction] = useFormState(signup, {
    success: false,
    error: null
  });
  
  return (
    <form action={formAction}>
      {state.error && <p className="error">{state.error}</p>}
      {state.success && <p className="success">Account created!</p>}
      
      <input name="username" required />
      <input name="email" type="email" required />
      <input name="password" type="password" required />
      
      <SubmitButton />
    </form>
  );
}

10. 性能优化策略

10.1 React 16-17 优化

  1. 虚拟列表渲染
import { FixedSizeList } from 'react-window';

function List({ items }) {
  const renderRow = ({ index, style }) => (
    <div style={style}>
      {items[index].text}
    </div>
  );
  
  return (
    <FixedSizeList
      height={500}
      width={300}
      itemCount={items.length}
      itemSize={35}
    >
      {renderRow}
    </FixedSizeList>
  );
}
  1. 使用React.memo避免不必要渲染
const ExpensiveComponent = React.memo(function ExpensiveComponent({ value }) {
  // 昂贵的渲染操作
  return <div>{value}</div>;
});
  1. 使用useMemo缓存计算值
function FilteredList({ items, filterTerm }) {
  const filteredItems = useMemo(() => {
    return items.filter(item => 
      item.name.toLowerCase().includes(filterTerm.toLowerCase())
    );
  }, [items, filterTerm]);
  
  return (
    <ul>
      {filteredItems.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

10.2 React 18 优化

  1. 使用useDeferredValue
function SearchResults({ query }) {
  // 延迟处理搜索结果更新
  const deferredQuery = useDeferredValue(query);
  
  // 昂贵的过滤操作使用deferredQuery
  const results = useMemo(() => {
    return computeFilteredResults(deferredQuery);
  }, [deferredQuery]);
  
  return (
    <div>
      <p>Searching for: {query}</p>
      <ul>
        {results.map(result => (
          <li key={result.id}>{result.title}</li>
        ))}
      </ul>
    </div>
  );
}
  1. useTransition分离紧急和非紧急更新
function TabContainer() {
  const [selectedTab, setSelectedTab] = useState('home');
  const [isPending, startTransition] = useTransition();
  
  function selectTab(tab) {
    // 标记为非紧急更新
    startTransition(() => {
      setSelectedTab(tab);
    });
  }
  
  return (
    <div>
      <TabButton 
        isSelected={selectedTab === 'home'}
        onClick={() => selectTab('home')}
      >
        Home
      </TabButton>
      <TabButton 
        isSelected={selectedTab === 'about'}
        onClick={() => selectTab('about')}
      >
        About
      </TabButton>
      
      {isPending ? <Spinner /> : <TabContent tab={selectedTab} />}
    </div>
  );
}

10.3 React 19 优化

  1. 使用自动记忆机制
// React 19能自动优化这样的纯渲染组件
function Item({ text }) {
  return <div>{text}</div>;
}

// 在父组件中不需要额外缓存处理
function ItemsList({ items }) {
  return (
    <div>
      {items.map(item => <Item key={item.id} text={item.text} />)}
    </div>
  );
}
  1. 使用useOptimistic实现乐观UI更新
function TodoList() {
  const [todos, setTodos] = useState([]);
  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (state, newTodo) => [...state, { ...newTodo, pending: true }]
  );
  
  async function addTodo(text) {
    const newTodo = { id: Date.now(), text };
    
    // 立即显示乐观结果
    addOptimisticTodo(newTodo);
    
    try {
      // 执行实际API调用
      const savedTodo = await saveTodoToServer(newTodo);
      // 成功后更新实际状态
      setTodos([...todos, savedTodo]);
    } catch (error) {
      // 处理错误...
    }
  }
  
  return (
    <div>
      <AddTodoForm onAdd={addTodo} />
      <ul>
        {optimisticTodos.map(todo => (
          <TodoItem 
            key={todo.id}
            todo={todo}
            isPending={todo.pending}
          />
        ))}
      </ul>
    </div>
  );
}

11. 路由与代码分割

11.1 React Router 演变

React Router 5 (React 16-17):

import { BrowserRouter, Route, Switch } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/users/:id" component={UserProfile} />
        <Route component={NotFound} />
      </Switch>
    </BrowserRouter>
  );
}

React Router 6 (React 18+):

import { BrowserRouter, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/users/:id" element={<UserProfile />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

11.2 代码分割

React 16.6+:

import React, { Suspense } from 'react';

// 懒加载组件
const LazyComponent = React.lazy(() => import('./LazyComponent'));

function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <LazyComponent />
      </Suspense>
    </div>
  );
}

React Router 6 与代码分割:

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import React, { Suspense } from 'react';

const Home = React.lazy(() => import('./routes/Home'));
const About = React.lazy(() => import('./routes/About'));
const UserProfile = React.lazy(() => import('./routes/UserProfile'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>Loading...</div>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/users/:id" element={<UserProfile />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

11.3 React 18+ 服务端组件

// app/page.js - 服务器组件
export default async function Page() {
  // 直接在服务器上获取数据,不需要useEffect
  const data = await fetchData();
  
  return (
    <div>
      <h1>Server Component</h1>
      <ServerRenderedContent data={data} />
      <ClientInteractiveComponent />
    </div>
  );
}

// ClientInteractiveComponent.js
'use client'; // 标记为客户端组件

import { useState } from 'react';

export default function ClientInteractiveComponent() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

12. 测试策略

12.1 组件测试

使用Jest和React Testing Library:

// Button.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';

test('renders button with correct text', () => {
  render(<Button>Click me</Button>);
  const buttonElement = screen.getByText(/click me/i);
  expect(buttonElement).toBeInTheDocument();
});

test('calls onClick when clicked', () => {
  const handleClick = jest.fn();
  render(<Button onClick={handleClick}>Click me</Button>);
  fireEvent.click(screen.getByText(/click me/i));
  expect(handleClick).toHaveBeenCalledTimes(1);
});

12.2 Hook测试

// useCounter.test.js
import { renderHook, act } from '@testing-library/react-hooks';
import useCounter from './useCounter';

test('should increment counter', () => {
  const { result } = renderHook(() => useCounter());
  
  act(() => {
    result.current.increment();
  });
  
  expect(result.current.count).toBe(1);
});

12.3 React 18+ 并发模式测试

// 测试useTransition
import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import TransitionComponent from './TransitionComponent';

test('shows loading state during transition', async () => {
  render(<TransitionComponent />);
  
  // 点击触发transition
  await userEvent.click(screen.getByText('Load Data'));
  
  // 检查是否显示加载状态
  expect(screen.getByText('Loading...')).toBeInTheDocument();
  
  // 等待transition完成
  await screen.findByText('Data loaded');
  
  // 检查加载状态是否消失
  expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
});

13. 版本迁移指南

13.1 从React 16到React 17

  1. 移除不安全生命周期方法

    • 重构 componentWillMountcomponentWillReceivePropscomponentWillUpdate
  2. 修复事件系统变化

    • 检查事件处理依赖于冒泡到document的代码
    • 事件池被移除,不再需要e.persist()
  3. JSX转换

    • 不再需要import React from 'react'(如果使用新JSX转换)

13.2 从React 17到React 18

  1. 更新渲染API

    // 旧版
    import ReactDOM from 'react-dom';
    ReactDOM.render(<App />, rootNode);
    
    // 新版
    import ReactDOM from 'react-dom/client';
    const root = ReactDOM.createRoot(rootNode);
    root.render(<App />);
    
  2. 处理自动批处理变化

    • 检查依赖于单独状态更新的代码
  3. 利用并发特性

    • 利用useTransition和useDeferredValue优化用户体验

13.3 从React 18到React 19

  1. 更新表单处理

    • 迁移到新表单APIs (useFormState, useFormStatus)
  2. 优化事件处理

    • 使用useEffectEvent分离事件逻辑
  3. 重构服务端组件

    • 更好地区分服务端和客户端组件

14. 综合最佳实践

14.1 组件设计

  • 使用函数组件和Hooks
  • 将复杂逻辑和状态抽离为自定义Hooks
  • 通过Props向下传递,通过Context共享全局状态
  • 避免过深的组件层级,考虑组合代替继承

14.2 状态管理

  • 局部状态使用useState和useReducer
  • 全局状态考虑使用Context或Redux
  • 使用不可变数据更新模式
  • React 18+中使用useDeferredValue和useTransition优化更新

14.3 副作用处理

  • 使用useEffect分类管理副作用
  • 正确设置依赖数组,避免遗漏
  • 使用清理函数防止内存泄漏
  • 使用fetch包装器如SWR或React Query优化数据获取

14.4 渲染优化

  • 使用React.memo包装纯组件
  • 使用useMemo缓存计算值
  • 使用useCallback稳定回调函数引用
  • 代码分割减少初始包大小
  • React 18+中使用startTransition标记非紧急更新

14.5 错误处理

  • 使用错误边界捕获组件树错误
  • 为Promise错误添加try/catch
  • 降级渲染处理错误状态

15. 总结

React 从16到19版本的演进展现了框架的不断成熟与优化。主要变化集中在:

  1. 编程模式变革: 从类组件到函数组件+Hooks的范式转变

  2. 响应式更新: 引入并发模式,更精细的更新优先级控制

  3. 服务器集成: 服务器组件的引入与完善

  4. 开发体验: 自动批处理、更好的工具和API设计

  5. 性能优化: 从手动优化到框架层自动优化

每个React版本都为开发者提供了更强大的工具来构建高性能、可维护的UI。通过理解这些变化和最佳实践,开发者可以充分利用React的强大能力,同时避免常见的陷阱。