【问题标题】:React Functional component: calling as function vs. as componentReact 函数式组件:作为函数调用与作为组件调用
【发布时间】:2018-04-08 11:38:04
【问题描述】:

假设我有一个功能组件:

const Foo = (props) => ( <div>{props.name}</div> );

和直接作为函数调用有什么区别:

const fooParent = () => (
    <div> {Foo({ name: "foo" })} </div>
)

与将其称为组件:

const fooParent = () => (
    <div> <Foo name="foo"/> </div>
)

我最感兴趣的是性能影响,React 如何在内部以不同的方式对待它们,以及 React Fiber 中的情况可能有何不同,我听说功能组件在其中得到了性能提升。

【问题讨论】:

标签: javascript reactjs


【解决方案1】:

如果直接调用函数式组件,实际上是在调用自定义钩子。

示例:

function A() {
  const [state] = useState([])
  return (
    <div>{state}</div>
  )
}

A()
<A />

如果你调用A()state 会挂载在父 Fiber 中,但是如果你使用 &lt;A /&gt;,React 将调用 createElement 来创建一个新的 Fiber,在其上挂载 state

【讨论】:

    【解决方案2】:

    实际上,当您将其作为组件调用时,会使用React.createElement() 创建一个新元素。另一方面,直接调用该函数。 所以,这就解释了为什么直接调用你的函数会更快。

    但请记住,在某些情况下,直接调用函数可能会导致像 @dmwong2268 在这里介绍的问题,或者像 this one

    【讨论】:

      【解决方案3】:

      所以我实际上遇到了一个用例,其中将渲染作为组件而不是函数调用是有益的。使用 React 16,您可以获得错误边界功能。这允许您在组件内引发错误时呈现回退错误 UI。事实证明,如果在函数调用变体中抛出异常,它不会触发componentDidCatch。它需要在子组件中抛出。

      class ErrorBoundary extends React.Component {
        constructor(props) {
          super(props);
      
          this.state = {
            error: false
          };
        }
      
        componentDidCatch() {
          this.setState({ error: true});
        }
      
        render() {
          return this.state.error
            ? "Error :("
            : this.props.children;
        }
      }
      
      const renderContent = () => {
        throw new Error();
      }
      
      const Content = () => {
        throw new Error();
      }
      
      // This will throw exception and not trigger error state
      const Foo = () => (
        <ErrorBoundary>
          <div>{renderContent()}</div>
        </ErrorBoundary>
      );
      
      // This will trigger the error state
      const Bar = () => (
        <ErrorBoundary>
          <div><Content /></div>
        </ErrorBoundary>
      );
      

      当然,您可以有一个更高的错误边界,但只是指出一个特定的用例,您可以在其中选择另一个。

      此外,出于命名目的,将其呈现为组件也很不错。它将在 React 开发工具中显示命名,并且您可以在进行 Enzyme 测试时使用该名称作为选择器。

      【讨论】:

        【解决方案4】:

        将它作为一个函数调用要快得多,事实上几个月前就有一个关于这个的讨论。在这一点上,功能性反应组件不能是PureComponents,因此没有真正应用额外的优化。

        基本上,如果您可以将功能组件称为消除整个反应生命周期的功能。如果您考虑一下,您现在可能正在您的渲染方法中使用这种技术。考虑一下:

        ... some component ... 
        
        render() {
        
          const tabHeaders =<TabHeaders>{this.props.tabs.map(this.renderTabHeader)}</TabHeader>;
          const tabContents = <TabContents>{this.props.tabs.map(this.renderTabContent)}</TabContents>;
        
          return (<div>
            {this.props.tabsBelow?[tabContents, tabHeaders] : [tabHeaders, tabContents]}
          </div>);
        } 
        

        renderTabHeader 方法返回一些反应组件,并且可能是功能组件,但在这种情况下只是一些组件类方法。

        详细解释见这篇文章:https://medium.com/missive-app/45-faster-react-functional-components-now-3509a668e69f

        还可以查看正在执行此操作的 babel 插件:https://babeljs.io/docs/plugins/transform-react-inline-elements

        【讨论】:

        • 你能总结一下为什么将它作为函数调用会更快吗?另外,如果没有缺点,那么为什么这样做不是标准呢?似乎大多数文档都指向您将功能组件称为组件。
        • 啊.. 那篇文章实际上是让我问这个问题的原因 XD。因此还有关于 React Fiber 的部分——那篇文章中有一条评论暗示已经对功能组件进行了优化以使其更快。我想知道这对结果有何影响。
        • 正确的方法是测量。但总的来说,你几乎没有比没有代码更快的速度。
        • 在较新版本的 react 中仍然是这种情况吗?
        • 如果你使用钩子,它的行为可能会很奇怪。见kentcdodds.com/blog/dont-call-a-react-function-component
        猜你喜欢
        • 2021-11-24
        • 2018-07-05
        • 1970-01-01
        • 2015-11-09
        • 2019-12-31
        • 1970-01-01
        • 1970-01-01
        • 2021-09-11
        • 2021-02-24
        相关资源
        最近更新 更多