Skip to content

第13章:React 进阶特性(提升开发效率)

React 提供了许多进阶特性,这些特性可以帮助我们编写更高效、更可维护的代码。本章将介绍一些常用的 React 进阶特性,包括组件复用技巧、性能优化、错误边界、懒加载和 Portals 等。

13.1 组件复用技巧(自定义Hooks、高阶组件HOC,了解即可)

13.1.1 自定义 Hooks

自定义 Hooks 是 React 16.8+ 引入的特性,它允许我们将组件逻辑提取到可重用的函数中。自定义 Hooks 的命名必须以 use 开头,这样 React 才能识别它们。

示例:创建一个 useCounter 自定义 Hook

jsx
// src/hooks/useCounter.js
import { useState, useCallback } from 'react';

export function useCounter(initialValue = 0, step = 1) {
  const [count, setCount] = useState(initialValue);

  const increment = useCallback(() => {
    setCount(prevCount => prevCount + step);
  }, [step]);

  const decrement = useCallback(() => {
    setCount(prevCount => prevCount - step);
  }, [step]);

  const reset = useCallback(() => {
    setCount(initialValue);
  }, [initialValue]);

  return { count, increment, decrement, reset };
}

使用自定义 Hook

jsx
import { useCounter } from '../hooks/useCounter';

function Counter() {
  const { count, increment, decrement, reset } = useCounter(0, 1);

  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}

export default Counter;

13.1.2 高阶组件(HOC)

高阶组件是一种设计模式,它接收一个组件并返回一个新的组件。HOC 可以用于添加额外的功能,如状态管理、认证、日志记录等。

示例:创建一个 withAuth 高阶组件

jsx
// src/hocs/withAuth.js
import React from 'react';

function withAuth(Component) {
  return function WithAuthComponent(props) {
    const isAuthenticated = localStorage.getItem('token') !== null;

    if (!isAuthenticated) {
      return <div>请先登录</div>;
    }

    return <Component {...props} />;
  };
}

export default withAuth;

使用高阶组件

jsx
import withAuth from '../hocs/withAuth';

function ProtectedPage() {
  return <div>这是受保护的页面</div>;
}

// 使用 withAuth 包装组件
export default withAuth(ProtectedPage);

13.2 memo、useMemo、useCallback(性能优化基础)

React 提供了几种性能优化的方法,包括 memouseMemouseCallback。这些方法可以帮助我们减少不必要的渲染,提高应用性能。

13.2.1 memo:组件缓存

memo 是一个高阶组件,它可以缓存组件的渲染结果,只有当组件的 props 发生变化时才重新渲染。

示例:使用 memo

jsx
import React, { memo } from 'react';

// 当 props 不变时,这个组件不会重新渲染
const ExpensiveComponent = memo(({ data }) => {
  console.log('ExpensiveComponent 渲染');
  // 模拟昂贵的计算
  const expensiveResult = data.reduce((acc, item) => acc + item, 0);
  return <div>结果: {expensiveResult}</div>;
});

function App() {
  const [count, setCount] = useState(0);
  const [data] = useState([1, 2, 3, 4, 5]);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Count: {count}</button>
      <ExpensiveComponent data={data} />
    </div>
  );
}

export default App;

13.2.2 useMemo:计算结果缓存

useMemo 是一个 Hook,它可以缓存计算结果,只有当依赖项发生变化时才重新计算。

示例:使用 useMemo

jsx
import React, { useState, useMemo } from 'react';

function App() {
  const [count, setCount] = useState(0);
  const [data] = useState([1, 2, 3, 4, 5]);

  // 只有当 data 变化时才重新计算
  const expensiveResult = useMemo(() => {
    console.log('计算 expensiveResult');
    return data.reduce((acc, item) => acc + item, 0);
  }, [data]);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Count: {count}</button>
      <div>结果: {expensiveResult}</div>
    </div>
  );
}

export default App;

13.2.3 useCallback:函数缓存

useCallback 是一个 Hook,它可以缓存函数,只有当依赖项发生变化时才重新创建函数。

示例:使用 useCallback

jsx
import React, { useState, useCallback } from 'react';
import ChildComponent from './ChildComponent';

function App() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  // 只有当 count 变化时才重新创建 handleClick 函数
  const handleClick = useCallback(() => {
    console.log('点击了按钮,count:', count);
  }, [count]);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Count: {count}</button>
      <input value={text} onChange={(e) => setText(e.target.value)} placeholder="输入文本" />
      <ChildComponent onClick={handleClick} />
    </div>
  );
}

export default App;

// ChildComponent.js
import React, { memo } from 'react';

const ChildComponent = memo(({ onClick }) => {
  console.log('ChildComponent 渲染');
  return <button onClick={onClick}>点击我</button>;
});

export default ChildComponent;

13.3 错误边界(Error Boundary,捕获组件错误)

错误边界是一种 React 组件,它可以捕获其子组件树中的 JavaScript 错误,记录错误,并显示备用 UI,而不是让整个应用崩溃。

创建错误边界组件

jsx
// src/components/ErrorBoundary.js
import React from 'react';

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error) {
    // 更新状态,下次渲染时显示备用 UI
    return { hasError: true, error };
  }

  componentDidCatch(error, errorInfo) {
    // 可以在这里记录错误信息
    console.error('错误边界捕获到错误:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 自定义备用 UI
      return (
        <div style={{ padding: '20px', backgroundColor: '#ffebee' }}>
          <h2>出错了</h2>
          <p>{this.state.error?.message}</p>
          <button onClick={() => this.setState({ hasError: false, error: null })}>
            重试
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

使用错误边界

jsx
import React, { useState } from 'react';
import ErrorBoundary from './components/ErrorBoundary';

function BuggyComponent() {
  const [count, setCount] = useState(0);

  if (count === 5) {
    // 模拟错误
    throw new Error('故意触发的错误');
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
    </div>
  );
}

function App() {
  return (
    <div>
      <h1>错误边界示例</h1>
      <ErrorBoundary>
        <BuggyComponent />
      </ErrorBoundary>
    </div>
  );
}

export default App;

13.4 懒加载(React.lazy、Suspense,优化页面加载速度)

React 提供了 React.lazySuspense 来实现组件的懒加载,这可以减小初始 bundle 的大小,提高页面加载速度。

13.4.1 基本使用

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

// 懒加载组件
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">首页</Link>
        <Link to="/about">关于</Link>
        <Link to="/contact">联系我们</Link>
      </nav>
      
      <Suspense fallback={<div>加载中...</div>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/contact" element={<Contact />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

export default App;

13.4.2 代码分割策略

  • 按路由分割:每个路由对应一个代码块
  • 按功能分割:将大型功能模块分割成单独的代码块
  • 按组件分割:将不常用的组件懒加载

13.5 Portals(传送门,解决DOM层级问题,如弹窗)

Portals 提供了一种将子组件渲染到父组件 DOM 层次结构之外的 DOM 节点的方法。这对于弹窗、模态框等需要突破父组件样式限制的场景非常有用。

示例:创建一个 Modal 组件

jsx
// src/components/Modal.js
import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';

function Modal({ isOpen, onClose, children }) {
  useEffect(() => {
    // 当模态框打开时,禁止背景滚动
    if (isOpen) {
      document.body.style.overflow = 'hidden';
    } else {
      document.body.style.overflow = 'unset';
    }

    return () => {
      document.body.style.overflow = 'unset';
    };
  }, [isOpen]);

  if (!isOpen) return null;

  return ReactDOM.createPortal(
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-content" onClick={(e) => e.stopPropagation()}>
        <button className="close-button" onClick={onClose}>
          ×
        </button>
        {children}
      </div>
    </div>,
    document.getElementById('modal-root') // 目标 DOM 节点
  );
}

export default Modal;

使用 Modal 组件

jsx
import React, { useState } from 'react';
import Modal from './components/Modal';

function App() {
  const [isModalOpen, setIsModalOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setIsModalOpen(true)}>打开模态框</button>
      
      <Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
        <h2>模态框标题</h2>
        <p>这是模态框的内容</p>
      </Modal>
    </div>
  );
}

export default App;

HTML 结构

html
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
    <!-- 用于 Portal 的目标节点 -->
    <div id="modal-root"></div>
  </body>
</html>

小结

本章介绍了 React 的一些进阶特性,包括:

  • 组件复用技巧:自定义 Hooks 和高阶组件
  • 性能优化:memo、useMemo 和 useCallback
  • 错误边界:捕获和处理组件错误
  • 懒加载:使用 React.lazy 和 Suspense 优化页面加载速度
  • Portals:解决 DOM 层级问题,适用于弹窗等场景

这些特性可以帮助我们编写更高效、更可维护的 React 应用。在实际项目中,我们应该根据具体需求选择合适的特性来使用,以达到最佳的开发效率和用户体验。

© 2026 编程马·菜鸟教程 版权所有