在花了一些时间学习 React 之后,我理解了创建组件的两种主要范式之间的区别。
我的问题是我什么时候应该使用哪一个,为什么?一个相对于另一个的好处/权衡是什么?
ES6 类:
import React, { Component } from 'react';
export class MyComponent extends Component {
render() {
return (
<div></div>
);
}
}
功能:
const MyComponent = (props) => {
return (
<div></div>
);
}
每当该组件没有状态可以操作时,我都在考虑功能,但是是这样吗?
我猜如果我使用任何生命周期方法,最好使用基于类的组件。
const MyComponent = (props) => <div>...</div>
新答案:在引入 React Hooks 之前,以下大部分内容都是正确的。
componentDidUpdate 可以使用 useEffect(fn) 进行复制,其中 fn 是在重新渲染时运行的函数。
可以使用 useEffect(fn, []) 复制 componentDidMount 方法,其中 fn 是在重新渲染时运行的函数,[] 是组件将重新渲染的对象数组,当且仅当至少有一个对象的值自之前的渲染。由于没有, useEffect() 在第一次安装时运行一次。
state 可以用 useState() 复制,它的返回值可以解构为 state 的引用和可以设置 state 的函数(即 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
顺便说一句,我听到很多人讨论出于性能原因不使用功能组件,特别是
“事件处理函数在功能组件中每次渲染都重新定义”
虽然确实如此,但请考虑您的组件是否真的以如此值得关注的速度或体积渲染。
如果是,您可以使用 useCallback
和 useMemo
钩子来防止重新定义函数。但是,请记住,这可能会使您的代码(微观上)worse in performance。
但老实说,我从未听说过重新定义函数是 React 应用程序的瓶颈。过早的优化是万恶之源——当它成为问题时要担心这一点。
老答案:你有正确的想法。如果您的组件除了接收一些道具和渲染之外没有做更多的事情,请使用功能性。您可以将这些视为纯函数,因为在给定相同道具的情况下,它们将始终呈现和表现相同。此外,他们不关心生命周期方法或拥有自己的内部状态。
因为它们是轻量级的,所以将这些简单的组件编写为功能组件是非常标准的。
如果您的组件需要更多功能,例如保持状态,请改用类。
更多信息:https://facebook.github.io/react/docs/reusable-components.html#es6-classes
2019 年 3 月更新
基于我原来的回答中所说的:
React 函数和类之间有什么根本区别吗?当然,有——在心智模型中。
https://overreacted.io/how-are-function-components-different-from-classes/
2019 年 2 月更新:
随着 React hooks 的引入,React 团队似乎希望我们尽可能使用函数式组件(这更好地遵循 JavaScript 的函数式本质)。
他们的动机:
1.) It’s hard to reuse stateful logic between components
2.) Complex components become hard to understand
3.) Classes confuse both people and machines
带有钩子的功能组件可以做almost类组件可以做的所有事情,而没有上面提到的任何缺点。
我建议尽快使用它们。
原始答案
函数式组件并不比基于类的组件更轻量级,“它们完全像类一样执行”。 - 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
,为您处理浅层 props 比较。 Read more
Always try to use stateless functions (functional components) whenever possible.
我读过十个人这样说。仍然没有人解释原因。
In an ideal world, most of your components would be stateless functions because in the future we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations. This is the recommended pattern, when possible.
更多信息:facebook.github.io/react/docs/…
从 React 17 开始,无状态功能组件这一术语具有误导性,应该避免使用(React.SFC deprecated,Dan Abramov on React.SFC),它们可以有状态,也可以有钩子(充当生命周期方法),它们或多或少与类组件重叠
基于类的组件
状态
生命周期方法
使用 React.PureComponent 进行记忆
功能组件:
状态(useState、useReducer 钩子)
生命周期方法(通过 useEffect、useLayoutEffect 挂钩)
通过 memo HOC 进行记忆
为什么我更喜欢功能组件
React 提供了 useEffect 钩子,这是一种非常清晰简洁的方式来组合 componentDidMount、componentDidUpdate 和 componentWillUnmount 生命周期方法
使用钩子,您可以提取可以在组件之间轻松共享和可测试的逻辑
减少范围界定的混淆
回答 motivation 为什么要使用钩子(即功能组件)。
我已将功能组件用于生产中的大量使用的应用程序。我只有一次将类组件用于“错误边界”,因为在功能组件中没有替代的“错误边界”。
我实际上只使用了一次“类组件”。
使用函数式表单更容易,因为您可以重用表单输入字段,并且可以使用 React 显示条件将它们分开。
类是一个不能分解或重用的重要组件。它们更适合功能繁重的组件,例如在弹出模块或其他东西中执行算法的组件。
最佳实践是功能组件的可重用性,然后使用小型功能组件组装完整的部分,例如导入到 React 表单文件中的表单输入字段。
另一个最佳实践是在执行此操作的过程中不要嵌套组件。
不定期副业成功案例分享
useState
钩子在功能组件中使用状态。Facebook officially recommends using functional components wherever possible
的信息?Facebook officially recommends using functional components wherever possible
感到好奇。