React中的函数式组件——现代开发标准

作者: IT Sectr 发布日期: 2026-07-04 阅读时间: 9 分钟

Functional Component — 是一种使用普通JavaScript函数创建React组件的方式,在React 16.8发布和Hook出现后成为标准。函数式组件接收props并返回React元素,无需继承React.Component。根据React, 2024的数据,React生态系统中97%的新组件都是作为函数编写的。

要点

  • Functional Component — 一个普通的JavaScript函数,接收props并返回JSX,无需extends Component。
  • Hook(useState、useEffect、useContext)为函数式组件提供对状态和生命周期的完全访问。
  • 可读性 — 函数式组件比类组件更简洁,没有构造函数、bind和多余的生命周期方法。
  • 性能 — React通过记忆化和懒初始化优化函数式组件。
  • React建议 — 所有新项目只使用带Hook的函数式组件。

什么是Functional Component?

Functional Component(函数式组件)——是定义为函数的React组件,接收props对象并返回React元素。在React 16.8之前,这类组件被称为stateless(无状态),因为它们无法管理内部状态。

随着React 16.8(2019年2月)中Hook的出现,函数式组件获得了对状态(useState)、副作用(useEffect)、上下文(useContext)和引用(useRef)的访问。术语“stateless”已经过时——现代函数式组件完全管理状态。

React官方推荐函数式组件作为创建组件的主要方式。react.dev上的所有文档、示例、教程和新API(Server Components、Actions)都基于函数式方法构建。类组件没有被移除,但React的新功能只能通过Hook使用。

创建函数式组件

最简单的函数式组件——一个返回JSX的函数。它接收props作为参数,不需要构造函数、super调用或this绑定。Props直接通过函数参数可用——没有this.props。

js
// Functional component without state
function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

// Arrow function (common variant)
const Greeting = ({ name }) => (
  <h1>Hello, {name}!</h1>
);

带状态的组件

要添加状态,请使用useState。Hook返回一个包含两个值的元组:当前状态和setter函数。与类的this.setState不同,useState完全替换值,而不与之前的值合并。

js
import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

函数式组件中的Hook

Hook是现代React的基础。每个Hook解决特定的任务:useState — 状态,useEffect — 副作用,useContext — 访问上下文,useRef — 对DOM元素的引用,useCallbackuseMemo — 记忆化。

useEffect处理副作用

useEffect同时替代了componentDidMount、componentDidUpdate和componentWillUnmount。第一个参数——带有effect的回调,第二个——依赖数组。如果数组为空,effect在挂载后执行一次。如果指定了依赖——在任何一个依赖发生变化时执行。

js
import React, { useState, useEffect } from 'react';

function UserData({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    let cancelled = false;

    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => {
        if (!cancelled) setUser(data);
      });

    // Cleanup - analog of componentWillUnmount
    return () => { cancelled = true; };
  }, [userId]); // Dependency

  return <div>{user?.name}</div>;
}

自定义Hook

函数式组件的主要优势——通过自定义Hook重用逻辑。如果在类组件中逻辑在componentDidMount和componentDidUpdate之间重复,在函数式组件中它被提取到以use为前缀的单独函数中。自定义Hook是HOC和render props的主要替代方案。

组合而非继承

React推荐组合而非继承来实现组件间代码复用。函数式组件非常适合组合:一个组件可以包含多个其他组件,向它们传递props和children。

children模式——当组件通过children prop接收子元素时——自然实现:function Layout({ children })。来自Vue的slots对应物通过命名的props-component实现:function Page({ header, sidebar, content })

继承在React中几乎不使用。即使是技术上支持extends的类组件,也很少形成超过一层深度的层次结构。使用组合的函数式组件完全满足了无需继承的代码复用需求。

性能优化

函数式组件具有内置的优化机制。React.memo — 类似于PureComponent的函数版本:如果props没有变化则阻止重新渲染(浅比较)。useMemo在渲染之间缓存计算结果。

js
import React, { useMemo } from 'react';

// React.memo - memoize component
const ExpensiveList = React.memo(({ items }) => {
  return <ul>
    {items.map(item =>
      <li key={item.id}>{item.name}</li>
    )}
  </ul>;
});

// useMemo - caching result
function Dashboard({ transactions }) {
  const summary = useMemo(() => {
    return transactions.reduce((acc, t) => acc + t.amount, 0);
  }, [transactions]);

  return <p>Total: {summary}</p>;
}

useCallback记忆化回调函数,使它们不会在每次渲染时重新创建。这在向包裹在React.memo中的子组件传递函数时至关重要:没有useCallback,子组件每次都会重新渲染,因为函数的引用发生了变化。

函数式与类组件的比较

两种方法之间的区别超出了语法。函数式组件——是一个闭包:状态和效果绑定到特定的渲染。类组件——是this:状态总是从this.state获取,而在异步回调执行时this.state可能已经过时。

方面函数式
状态useState(隔离的)this.state + this.setState
效果useEffect带依赖componentDidMount + componentDidUpdate
上下文useContextContext.Consumer或static contextType
优化React.memo + useMemoPureComponent + shouldComponentUpdate
逻辑重用自定义HookHOC / Render props
this构造函数中bind

函数式组件提供线性执行流程。在类组件中,相关逻辑常常分散在多个生命周期方法中。例如,WebSocket订阅在componentDidMount中设置并在componentWillUnmount中移除。在函数式组件中,两个操作——都在一个带有清理函数的useEffect中。

TypeScript与函数式组件

TypeScript与函数式组件比与类协作更自然。Props类型化 — 函数参数的接口。返回值的类型 — JSX.ElementReactNode。useState自动从初始值推断类型。

ts
import React, { useState } from 'react';

interface ButtonProps {
  label: string;
  variant?: 'primary' | 'secondary';
  onClick: () => void;
}

const Button: React.FC<ButtonProps> =
  ({ label, variant = 'primary', onClick }) => (
    <button
      className={`btn btn-${variant}`}
      onClick={onClick}
    >
      {label}
    </button>
  );

现代TypeScript允许使用区分联合类型对useReducer进行类型化,在自定义Hook中使用泛型,以及通过React.PropsWithChildren严格检查children。函数式组件与TypeScript在无需额外抽象的情况下提供完整的类型安全性。

常见问题

为什么React推荐函数式组件?

函数式组件更简单:没有构造函数、bind、this和分散的生命周期方法。Hook允许将相关逻辑分组。React将资源投入在Hook的开发上,而非类。Server Components和新功能仅在函数中可用。

可以在函数式组件中使用setInterval吗?

可以,通过useEffect内部设置定时器并带清理函数来清除clearInterval。请注意闭包:如果你要递增计数器,请使用setState的函数形式:setCount(c => c + 1),以避免依赖闭包中的旧值。

如何在函数式组件中获取之前的props?

使用useRef存储之前的值。在每次渲染后在useEffect中更新ref。对于视觉比较,自定义Hook usePrevious可以提供帮助:const prev = usePrevious(value);

React.memo和useMemo有什么区别?

React.memo — 用于记忆化整个组件的HOC:如果props没有变化则阻止重新渲染。useMemo — 用于记忆化组件内特定值的Hook:基于依赖数组在渲染之间缓存函数的结果。

如何在函数式组件中模拟getDerivedStateFromProps?

最简单的方法 — useState从props初始化 + useEffect依赖props进行同步。对于罕见情况,React建议:在输入数据变化时使用key强制重新创建组件。

总结

  • Functional Component — 现代React标准:接收props并返回JSX的函数。
  • Hook(useState、useEffect)为函数式组件提供状态、效果和上下文,无需类。
  • 组合通过children和自定义Hook替代了继承和HOC模式。
  • React.memo和useMemo确保了函数式组件的性能优化。
  • TypeScript与函数式组件提供了严格的props类型化和自动类型推断。
  • useEffect在一个地方替代了类组件的所有生命周期方法。
  • 自定义Hook允许在组件之间提取和重用逻辑,无需HOC。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读