React 函数式无状态组件、PureComponent、Component;有什么区别,我们什么时候应该使用什么? [英] React functional stateless component, PureComponent, Component; what are the differences and when should we use what?

查看:50
本文介绍了React 函数式无状态组件、PureComponent、Component;有什么区别,我们什么时候应该使用什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

React v15.3.0 中了解到,我们有一个名为 PureComponent 的新基类,可以通过内置的 PureRenderMixin 进行扩展.我的理解是,在幕后,这在 shouldComponentUpdate 中使用了对 props 的浅层比较.

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

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

很久以前,我们曾经将无状态组件称为纯组件,甚至是哑组件.似乎纯"这个词的整个定义都没有.现在在 React 中发生了变化.

虽然我了解这三者之间的基本区别,但我仍然不确定何时选择.另外,每种方法的性能影响和权衡是什么?


更新:

这些是我希望得到澄清的问题:

  • 我应该选择将我的简单组件定义为函数式(为了简单起见)还是扩展 PureComponent 类(为了性能)?
  • 性能提升是否是我为我失去了简单?
  • 当我总是可以使用 PureComponent 以获得更好的性能时,我是否需要扩展普通的 Component 类?

解决方案

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

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

  • 对于需要轻松重用的简单的、仅展示性的组件,首选无状态功能组件.通过这种方式,您可以确保它们与实际的应用程序逻辑分离,它们非常容易测试并且它们没有意外的副作用.例外情况是,如果由于某种原因你有很多,或者你真的需要优化它们的渲染方法(因为你不能为无状态功能组件定义 shouldComponentUpdate).

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

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

    class MyComponent 扩展组件 {shouldComponentUpdate (nextProps, nextState) {返回 !_.isEqual(this.props, nextProps) ||!_.isEqual(this.state, nextState);}}

此外,实现自己的 shouldComponentUpdate 或从 PureComponent 扩展都是优化,和往常一样,只有在遇到性能问题(避免过早优化).根据经验,我总是在应用程序处于工作状态后尝试进行这些优化,大多数功能已经实现.当性能问题真正成为障碍时,专注于它们会容易得多.

更多详情

功能性无状态组件:

这些只是使用函数定义的.由于无状态组件没有内部状态,因此输出(呈现的内容)仅取决于作为此函数输入的道具.

优点:

  • 在 React 中定义组件的最简单方法.如果您不需要管理任何状态,为什么还要为类和继承而烦恼呢?函数和类之间的主要区别之一是,对于函数,您可以确定输出仅取决于输入(而不取决于之前执行的任何历史记录).

  • 理想情况下,在您的应用程序中,您的目标应该是拥有尽可能多的无状态组件,因为这通常意味着您将逻辑移到视图层之外并将其移至诸如 redux 之类的东西,这意味着您可以测试真正的无需渲染任何内容(更容易测试、更可重用等).

缺点:

  • 没有生命周期方法.你没有办法定义 componentDidMount 和其他朋友.通常,您在层次结构中较高的父组件中执行此操作,以便您可以将所有子组件转换为无状态组件.

  • 无法手动控制何时需要重新渲染,因为您无法定义 shouldComponentUpdate.每次组件收到新的 props 时都会重新渲染(无法进行浅层比较等).将来,React 可以自动优化无状态组件,现在有一些库可以使用.由于无状态组件只是函数,基本上就是函数记忆"的经典问题.

  • 不支持引用:https://github.com/facebook/react/问题/4936

扩展 PureComponent 类的组件 VS 扩展 Component 类的普通组件:

React 曾经有一个 PureRenderMixin,您可以将其附加到使用 React.createClass 语法定义的类.mixin 将简单地定义一个 shouldComponentUpdate 在下一个 props 和下一个 state 之间执行浅比较,以检查是否有任何更改.如果没有任何变化,则无需执行重新渲染.

如果要使用 ES6 语法,则不能使用 mixins.所以为了方便,React 引入了一个 PureComponent 类,你可以继承它而不是使用 Component.PureComponent 只是以与 PureRendererMixin 相同的方式实现了 shouldComponentUpdate.这主要是一件方便的事情,因此您不必自己实现它,因为当前/下一个状态和 props 之间的浅薄比较可能是最常见的场景,可以让您快速获得性能优势.

示例:

class UserAvatar 扩展组件 {使成为() {返回<div><img src={this.props.imageUrl}/>{{ this.props.username }} </div>}}

如您所见,输出取决于 props.imageUrlprops.username.如果在父组件中使用相同的 props 渲染 <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg"/>,React 会调用 render 每次,即使输出完全相同.请记住,尽管 React 实现了 dom diffing,因此 DOM 不会实际更新.尽管如此,执行 dom diffing 可能很昂贵,所以在这种情况下,这将是一种浪费.

如果 UserAvatar 组件扩展了 PureComponent,则执行浅比较.并且因为 props 和 nextProps 是一样的,render 根本不会被调用.

React 中纯"的定义注意事项:

一般来说,纯函数"是在给定相同输入的情况下始终评估为相同结果的函数.输出(对于 React,这是 render 方法返回的内容)不依赖于任何历史/状态,也没有任何副作用(改变外部世界"的操作)函数).

在 React 中,如果您将无状态"称为从不调用 this.setState 并且不使用 this 的组件,则根据上述定义,无状态组件不一定是纯组件.状态.

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

哑组件"定义有一个更实用"的含义(至少在我的理解中):一个哑组件通过 props 被告知"父组件要做什么,并且不知道如何做事但使用道具回调代替.

智能"AvatarComponent 示例:

class AvatarComponent extends Component {展开头像(){this.setState({loading: true });sendAjaxRequest(...).then(() => {this.setState({loading: false });});}使成为 () {<div onClick={this.expandAvatar}><img src={this.props.username}/>

}}

哑巴"示例AvatarComponent:

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

}}

最后我会说愚蠢"、无状态"和纯"是完全不同的概念,有时可能会重叠,但不一定,这主要取决于您的用例.

Came to know that from React v15.3.0, we have a new base class called PureComponent to extend with PureRenderMixin built-in. What I understand is that, under the hood this employs a shallow comparison of props inside shouldComponentUpdate.

Now we have 3 ways to define a React component:

  1. Functional stateless component which doesn't extend any class
  2. A component that extends PureComponent class
  3. A normal component that extends Component class

Some time back we used to call stateless components as Pure Components, or even Dumb Components. Seems like the whole definition of the word "pure" has now changed in React.

Although I understand basic differences between these three, I am still not sure when to choose what. Also what are the performance impacts and trade-offs of each?


Update:

These are the question I expect to get clarified:

  • Should I choose to define my simple components as functional (for the sake of simplicity) or extend PureComponent class (for performance sake)?
  • Is the performance boost that I get a real trade-off for the simplicity I lost?
  • Would I ever need to extend the normal Component class when I can always use PureComponent for better performance?

解决方案

How do you decide, how do you choose between these three based on the purpose/size/props/behaviour of our components?

Extending from React.PureComponent or from React.Component with a custom shouldComponentUpdate method have performance implications. Using stateless functional components is an "architectural" choice and doesn't have any performance benefits out of the box (yet).

  • For simple, presentational-only components that need to be easily reused, prefer stateless functional components. This way you're sure they are decoupled from the actual app logic, that they are dead-easy to test and that they don't have unexpected side effects. The exception is if for some reason you have a lot of them or if you really need to optimise their render method (as you can't define shouldComponentUpdate for a stateless functional component).

  • Extend PureComponent if you know your output depends on simple props/state ("simple" meaning no nested data structures, as PureComponent performs a shallow compare) AND you need/can get some performance improvements.

  • Extend Component and implement your own shouldComponentUpdate if you need some performance gains by performing custom comparison logic between next/current props and state. For example, you can quickly perform a deep comparison using 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>
    }
} 

As you can see the output depends on props.imageUrl and props.username. If in a parent component you render <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" /> with the same props, React would call render every time, even if the output would be exactly the same. Remember though that React implements dom diffing, so the DOM would not be actually updated. Still, performing the dom diffing can be expensive, so in this scenario it would be a waste.

If the UserAvatar component extends PureComponent instead, a shallow compare is performed. And because props and nextProps are the same, render will not be called at all.

Notes on the definition of "pure" in React:

In general, a "pure function" is a function that evaluates always to the same result given the same input. The output (for React, that's what is returned by the render method) doesn't depend on any history/state and it doesn't have any side-effects (operations that change the "world" outside of the function).

In React, stateless components are not necessarily pure components according to the definition above if you call "stateless" a component that never calls this.setState and that doesn't use this.state.

In fact, in a PureComponent, you can still perform side-effects during lifecycle methods. For example you could send an ajax request inside componentDidMount or you could perform some DOM calculation to dynamically adjust the height of a div within render.

The "Dumb components" definition has a more "practical" meaning (at least in my understanding): a dumb component "gets told" what to do by a parent component via props, and doesn't know how to do things but uses props callbacks instead.

Example of a "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>
    }
}

Example of a "dumb" AvatarComponent:

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

In the end I would say that "dumb", "stateless" and "pure" are quite different concepts that can sometimes overlap, but not necessarily, depending mostly on your use case.

这篇关于React 函数式无状态组件、PureComponent、Component;有什么区别,我们什么时候应该使用什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆