何时使用基于 ES6 类的 React 组件与功能性 ES6 React 组件

When to use ES6 class based React components vs. functional ES6 React components?

本文关键字:组件 React ES6 功能性 何时使 类的      更新时间:2023-09-26

花了一些时间学习 React 后,我理解了创建组件的两种主要范式之间的区别。

我的问题是我什么时候应该使用哪一个,为什么?两者相比有什么好处/权衡?


ES6 类:

import React, { Component } from 'react';
export class MyComponent extends Component {
  render() {
    return (
      <div></div>
    );
  }
}

功能的:

const MyComponent = (props) => {
    return (
      <div></div>
    );
}

每当该组件没有要操纵的状态时,我就会想到功能,但事实就是这样吗?

我猜如果我使用任何生命周期方法,最好使用基于类的组件。

新答案:在引入 React Hooks 之前,以下大部分内容都是正确的。

  • componentDidUpdate可以用useEffect(fn)复制,其中fn是在重新渲染时运行的函数。

  • componentDidMount方法都可以使用 useEffect(fn, []) 进行复制,其中 fn 是重新渲染时运行的函数,[] 是组件将重新渲染的对象数组,当且仅当自上次渲染以来至少有一个值发生了变化。由于没有,useEffect()在第一次装载时运行一次。

  • state可以用useState()复制,其返回值可以解构为状态的引用和可以设置状态的函数(即const [state, setState] = useState(initState))。一个例子可以更清楚地解释这一点:

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

顺便说一句,我听到很多人讨论出于性能原因不使用功能组件,特别是

"事件处理函数在功能组件中按渲染重新定义"

虽然是真的,但请考虑您的组件是否真的以值得关注的速度或体积渲染。

如果是,则可以防止使用 useCallbackuseMemo 钩子重新定义函数。但是,请记住,这可能会使您的代码(微观上)性能变差。

但老实说,我从未听说过重新定义函数是 React 应用程序中的瓶颈。 过早的优化是万恶之源 - 当它出现问题时,请担心这一点。

<小时 />

老答案:你有正确的想法。如果您的组件除了采用一些道具和渲染之外没有做更多的事情,请使用功能。您可以将这些视为纯函数,因为它们在给定相同的道具时将始终呈现和行为相同。此外,他们不关心生命周期方法或有自己的内部状态。

因为它们是轻量级的,所以将这些简单的组件编写为功能组件是非常标准的。

如果您的组件需要更多功能(如保持状态),请改用类。

更多信息: https://facebook.github.io/react/docs/reusable-components.html#es6-classes

> 2023 年 1 月更新

TLDR;函数是创建组件的最佳方式。 React.Component是一个遗留的API。

"我们建议将组件定义为函数而不是类。

"React 仍然支持类组件,但我们不建议在新代码中使用它们。"

https://react.dev/reference/react/Component

2019年3月更新

基于我最初回答中的内容:

React

函数和 React 函数之间有什么根本区别吗上课吗?当然,在心智模型中也有。

https://overreacted.io/how-are-function-components-different-from-classes/

2019年2月更新:

随着 React 钩子的引入,React 团队似乎希望我们尽可能使用函数式组件(这更好地遵循了 JavaScript 的功能性质)。

他们的动机:

    很难在
  1. 组件之间重用有状态逻辑。
  2. 复杂的组件变得难以理解。
  3. 类混淆了人和机器。

带有钩子的功能组件几乎可以完成类组件可以做的所有事情,而没有上面提到的任何缺点。

我建议您尽快使用它们。

原始答案(2018年4月)

功能组件并不比基于类的组件更轻量级,"它们的性能与类完全相同 https://github.com/facebook/react/issues/5677#issuecomment-241190513。

上面的链接有点过时,但 React 16.7.0 的文档说功能和类组件:

从 React 的角度来看是等价的

https://reactjs.org/docs/components-and-props.html#stateless-functions

功能组件和仅实现 render 方法(语法除外)的类组件之间基本上没有区别。

将来(引用上面的链接):

我们 [React] 可能会添加这样的优化

如果您尝试通过消除不必要的渲染来提高性能,这两种方法都提供支持。 memo用于功能组件,PureComponent用于类。

https://reactjs.org/docs/react-api.html#reactmemo

https://reactjs.org/docs/react-api.html#reactpurecomponent

这真的取决于你。如果您想要更少的样板,请去功能。如果你喜欢函数式编程,不喜欢类,那就去函数式。如果您希望代码库中的所有组件保持一致,请使用类。如果你厌倦了在需要类似 state 的东西时从函数式组件重构为基于类的组件,那就去找类吧。

尽可能始终尝试使用无状态函数(功能组件)。在某些情况下,您需要使用常规的 React 类:

  • 组件需要维护状态
  • 组件重新渲染太多,您需要通过shouldComponentUpdate
  • 您需要一个容器组件

更新

现在有一个名为 PureComponent 的 React 类,您可以扩展(而不是 Component ),它实现了自己的shouldComponentUpdate,为您处理浅层道具比较。阅读更多

从 React 17 开始,术语无状态功能组件具有误导性,应该避免使用(React.SFC 已弃用,Dan Abramov 在 React.SFC 上),它们可以有一个状态,它们可以有钩子(充当生命周期方法),它们或多或少地与类组件重叠

基于类的组件

  • 生命周期方法
  • 使用 React.PureComponent 进行记忆

功能部件:

  • state (useState, useReducer hooks)
  • 生命周期方法(通过 useEffect、useLayoutEffect 钩子)
  • 通过备忘录HOC进行记忆

为什么我更喜欢功能组件

  • React 提供了 useEffect 钩子,这是一种非常清晰简洁的方式来结合componentDidMountcomponentDidUpdatecomponentWillUnmount生命周期方法
  • 使用钩子,您可以提取可以在组件之间轻松共享可测试的逻辑
  • 减少对范围界定的混淆

对为什么使用钩子(即功能组件)做出反应

我已经在生产中大量使用的应用程序中使用了功能组件。只有一次我将类组件用于"错误边界",因为功能组件中没有替代的"错误边界"。

我实际上只使用了一次"类组件"。

基于类的组件提供了一种更加结构化和有条理的方式来定义和实现组件,并且它们提供了其他特性和功能,例如使用本地状态和生命周期方法的能力。这可以使它们成为创建需要大量逻辑和功能的复杂组件的不错选择。

另一方面,功能组件更简单、更易于使用,并且由于它们更轻量级,因此性能更高。它们也更容易测试和调试,因为它们是没有副作用的纯函数。这使它们成为创建不需要大量逻辑或状态管理的简单组件的不错选择。

尽管函数组件中有很多有用的特性,但在某些情况下,我们需要在函数组件上使用类组件:

1. 误差边界

当发生错误时,通常会在屏幕上显示崩溃的组件树,这可能会给最终用户留下不好的印象。

错误边界帮助我们处理这种情况。错误边界允许我们在组件内部发生错误时显示回退 UI。

我们可以使用错误边界来跟踪客户端错误,并为调试提供有用的信息。无法使用函数组件创建误差边界。

错误边界只能使用类组件实现。

2. 纯成分

很多时候,我们需要根据某些标准避免不必要的组件重新渲染。

在这种情况下,我们可以通过扩展 React.PureComponent 来使用纯组件,因为它们有一个静态方法"shouldComponentUpdate",它根据组件的 props 和状态决定是否重新渲染。

很多时候 react js 开发人员使用 "useMemo" 钩子来避免不必要的重新渲染,如下所示:

   const App=()=>{
                return useMemo(()=>{
                    return <>{/*Code*/}</>
                  },[dependancies])
              }

但是,当组件更新其状态时,"useMemo"钩子之前的所有内容仍将执行。因此,"useMemo"可能并不总是避免不必要的重新渲染的最佳选择。

由于我们不能使用函数组件创建纯组件,因此我们需要使用类组件。

3. 生命周期方法

我知道情况不应该如此,因为大多数生命周期方法(如 componentDidMount、componentDidUpdate 或 componentWillUnmount)都可以替换为 useEffect。

重要的是要注意,useEffect 钩子在依赖关系和避免无限循环时需要格外小心。

并且确保依赖项不会从钩子内部更新(这可能会导致无限循环)

在类组件中,当发生某些事件(例如,装载、更新或卸载组件时)时,将自动执行生命周期方法。

表单使用函数更容易,因为您可以重用表单输入字段,并且可以使用 React 显示条件将它们分开。

类是一个无法分解或重用的重要组件。它们更适合功能繁重的组件,例如在弹出模块或其他模块中执行算法的组件。

最佳实践是功能组件的可重用性,然后使用小型功能组件来组装完整的部分,例如将表单输入字段导入到 React 表单的文件中。

另一个最佳做法是在执行此操作的过程中不要嵌套组件。