React功能无状态组件,PureComponent, component;区别是什么,什么时候应该用什么?

反应v15.3.0开始,我们有了一个名为PureComponent的新基类来扩展内置的PureRenderMixin。我所理解的是,在引子下面,这采用了shouldComponentUpdate内的道具的浅比较。

现在我们有3种方法来定义React组件:

  1. 不扩展任何类的功能性无状态组件
  2. 扩展PureComponent类的组件
  3. 扩展Component类的普通组件

很久以前,我们把无状态组件称为纯组件,甚至是哑组件。似乎“纯粹”这个词的整个定义;现在在React中有所改变。

虽然我了解这三者之间的基本区别,但我仍然不确定什么时候选择什么。另外,它们对性能的影响和权衡是什么?


更新:

以下是我希望得到澄清的问题:

  • 我应该选择将我的简单组件定义为函数式(为了简单)还是扩展PureComponent类(为了性能)?
  • 是性能提升,我得到了一个真正的权衡 我失去的简单?李< / >
  • 当我总是可以使用PureComponent来获得更好的性能时,我是否需要扩展正常的Component类?
43805 次浏览

你如何决定,你如何根据我们组件的目的/大小/道具/行为在这三者之间做出选择?

使用自定义shouldComponentUpdate方法从React.PureComponentReact.Component扩展有性能影响。使用无状态功能组件是一种“架构”选择,(目前)没有任何开箱即用的性能优势。

  • 对于需要易于重用的简单、仅呈现的组件,建议使用无状态的功能组件。通过这种方式,你可以确保它们与实际的应用逻辑分离,它们非常容易测试,并且它们没有意想不到的副作用。例外情况是,如果出于某种原因,你有很多 的它们,或者如果你真的需要优化它们的呈现方法(因为你不能为无状态功能组件定义shouldComponentUpdate)。

  • 如果你知道你的输出依赖于简单的props/state(“简单”意味着没有嵌套的数据结构,因为PureComponent执行一个浅比较)并且你需要/可以得到一些性能改进,那么扩展PureComponent

  • 如果你需要通过在next/current props和state之间执行自定义比较逻辑来获得性能提升,则扩展Component并实现自己的shouldComponentUpdate。例如,你可以使用lodash#isEqual快速执行深度比较:

    class MyComponent extends Component {
    shouldComponentUpdate (nextProps, nextState) {
    return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
    }
    }
    

Also, implementing your own shouldComponentUpdate or extending from PureComponent are optimizations, and as usual you should start looking into that only if you have performance issues (avoid premature optimizations). As a rule of thumb, I always try to do these optimisations after the application is in a working state, with most of the features already implemented. It's a lot easier to focus on performance problems when they actually get in the way.

More details

Functional stateless components:

These are defined just using a function. Since there's no internal state for a stateless component, the output (what's rendered) only depends on the props given as input to this function.

Pros:

  • Simplest possible way of defining a component in React. If you don't need to manage any state, why bother with classes and inheritance? One of the main differences between a function and a class is that with the function you are sure the output depends only on the input (not on any history of the previous executions).

  • Ideally in your app you should aim to have as many stateless components as possible, because that normally means you moved your logic outside of the view layer and moved it to something like redux, which means you can test your real logic without having to render anything (much easier to test, more reusable, etc.).

Cons:

  • No lifecycle methods. You don't have a way to define componentDidMount and other friends. Normally you do that within a parent component higher in the hierarchy so you can turn all the children into stateless ones.

  • No way to manually control when a re-render is needed, since you can't define shouldComponentUpdate. A re-render happens every time the component receives new props (no way to shallow compare, etc.). In the future, React could automatically optimise stateless components, for now there's some libraries you can use. Since stateless components are just functions, basically it's the classic problem of "function memoization".

  • Refs are not supported: https://github.com/facebook/react/issues/4936

A component that extends PureComponent class VS A normal component that extends Component class:

React used to have a PureRenderMixin you could attach to a class defined using React.createClass syntax. The mixin would simply define a shouldComponentUpdate performing a shallow comparison between the next props and the next state to check if anything there changed. If nothing changes, then there's no need to perform a re-render.

If you want to use the ES6 syntax, you can't use mixins. So for convenience React introduced a PureComponent class you can inherit from instead of using Component. PureComponent just implements shouldComponentUpdate in the same way of the PureRendererMixin. It's mostly a convenience thing so you don't have to implement it yourself, as a shallow comparison between current/next state and props is probably the most common scenario that can give you some quick performance wins.

Example:

class UserAvatar extends Component {
render() {
return <div><img src={this.props.imageUrl} /> \{\{ this.props.username }} </div>
}
}

如你所见,输出依赖于props.imageUrlprops.username。如果在父组件中使用相同的道具渲染<UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />, React每次都会调用render,即使输出完全相同。记住,React实现了dom差分,所以dom实际上不会被更新。不过,执行dom差分的代价可能很高,因此在这种情况下,这将是一种浪费。

如果UserAvatar组件扩展了PureComponent,则执行浅比较。因为props和nextProps是相同的,所以根本不会调用render

关于React中“pure”的定义:

一般来说,“纯函数”是在相同输入条件下,计算结果总是相同的函数。输出(对于React,这是render方法返回的内容)不依赖于任何历史/状态,也没有任何副作用(改变函数外部“世界”的操作)。

在React中,如果你将一个从未调用this.setState且不使用this.state的组件称为“无状态”,那么根据上面的定义,无状态组件不一定是纯组件。

事实上,在PureComponent中,你仍然可以在生命周期方法期间执行副作用。例如,你可以在componentDidMount中发送一个ajax请求,或者你可以执行一些DOM计算来动态调整render中div的高度。

“哑巴组件”的定义有一个更“实用”的含义(至少在我的理解中):哑巴组件通过props被父组件“告知”要做什么,并且不知道如何做事情,而是使用props回调。

“smart”AvatarComponent的例子:

class AvatarComponent extends Component {
expandAvatar () {
this.setState({ loading: true });
sendAjaxRequest(...).then(() => {
this.setState({ loading: false });
});
}


render () {
<div onClick={this.expandAvatar}>
<img src={this.props.username} />
</div>
}
}

“dumb”AvatarComponent的例子:

class AvatarComponent extends Component {
render () {
<div onClick={this.props.onExpandAvatar}>
{this.props.loading && <div className="spinner" />}
<img src={this.props.username} />
</div>
}
}

最后,我想说“哑”、“无状态”和“纯”是完全不同的概念,它们有时会重叠,但不一定,这主要取决于您的用例。

我不是一个反应过度的天才,但根据我的理解,我们可以在以下情况下使用每个组件

  1. 这些是没有生命周期的组件,所以这些组件应该用于渲染父组件的repeat元素,例如渲染文本列表,它只显示信息,不执行任何操作。

  2. 纯组件——这些是具有生命周期的项目,当给定一组特定道具时,它们总是会返回相同的结果。这些组件可以在显示结果列表或特定对象数据时使用,这些对象数据没有复杂的子元素,用于执行只影响自身的操作。这种显示用户卡片列表或产品卡片列表(基本产品信息),用户可以执行的唯一操作是单击查看详细页面或添加到购物车。

  3. 我使用术语复杂组件,因为它们通常是页面级组件,由许多子组件组成,因为每个子组件都可以以自己独特的方式表现,所以你不能100%确定它会在给定状态下呈现相同的结果。正如我所说,通常这些应该用作容器组件

  • React.Component是默认的“normal”组件。你可以使用class关键字和extends React.Component声明它们。把它们看作一个类,具有生命周期方法、事件处理程序和任何方法。

  • React.PureComponent是一个React.Component,它通过一个函数实现了shouldComponentUpdate(),该函数对其propsstate进行了简单的比较。你必须使用forceUpdate(),如果你知道组件有道具或状态嵌套数据改变,你想重新呈现。所以当你传递数组或对象作为道具或设置状态改变时,如果你需要组件重新渲染,它们就不太好了。

  • 功能组件是没有生命周期函数的组件。它们应该是无状态的,但它们是如此漂亮和干净,我们现在有钩子(自React 16.8以来),所以你仍然可以有一个状态。所以我猜它们只是“干净的组件”。