Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Functional component: calling as function vs. as component

Say I have a functional component:

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

What is the difference between calling it directly as a function:

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

versus calling it as a component:

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

I'm mostly interested in performance implications, how React treats them differently internally, and perhaps how things might be different in React Fiber, where I hear functional components got a performance boost.

like image 735
dmwong2268 Avatar asked Oct 26 '17 23:10

dmwong2268


People also ask

Can we call React component as function?

From React documentation we know that component is just a plain JS class or function that eventually returns JSX (most of the time).

Should I use React component or function?

Although we should prefer using function components most times as React also allows using state with function components in React version 16.8 and later by using the useState hook, which is also recommended by Meta (Facebook App) itself.

Which is better function component or class component in React?

Nothing is better, because both have pros and cons. But class components are important to understand React flow and lifecycle methods. The new learner should practice React using class components. Once they are familiar with class components, they can learn and use functional components.

What is the difference between component and function in React?

The most obvious one difference is the syntax. A functional component is just a plain JavaScript function which accepts props as an argument and returns a React element. A class component requires you to extend from React. Component and create a render function which returns a React element.


4 Answers

Calling it as a function is much faster, in fact there was a talk exactly about this few months ago. At this point functional react components can't be PureComponents so no extra optimizations really applied to them.

Basically if you can call functional component as a function that eliminates whole react lifecycle. If you think about it you are probably using this technique inside your render method even right now. Consider this:

... 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 method returns some react components, and could've been functional components but in this case is just some component class method.

See this article for detailed explanation: https://medium.com/missive-app/45-faster-react-functional-components-now-3509a668e69f

Also check out this babel plugin that is doing that: https://babeljs.io/docs/plugins/transform-react-inline-elements

like image 148
vittore Avatar answered Oct 08 '22 21:10

vittore


So I actually ran into a use case where it is beneficial to render as a component rather than a function call. With React 16, you get the error boundaries feature. This allows you to render fallback error UIs when an error is thrown within the component. Turns out, if an exception is thrown in the function call variation, it won't trigger componentDidCatch. It needs to be thrown within a child component.

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>
);

Of course, you could have an error boundary higher up, but just pointing out one specific use case where you may choose one of the other.

Also, it's nice to render as component for naming purposes. It will show up named in React dev tools, and also you can use the name as selectors when you do Enzyme testing.

like image 21
dmwong2268 Avatar answered Oct 08 '22 22:10

dmwong2268


If you call functional component directly, you are calling a custom hook actually.

example:

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

A()
<A />

If you call A(), the state mounts in parent fiber, but if you use <A />, React will call createElement to create a new fiber on which to mount state.

like image 10
Liuyl Avatar answered Oct 08 '22 21:10

Liuyl


Actually, when you call it as a component a new element is created with React.createElement(). On the other hand, the function is called directly. So, that explains a little bit why calling your function directly is faster.

But keep in mind, that in some cases calling a function directly may cause problems like the one introduced by @dmwong2268 here, or like this one

like image 4
Idriss AIT HAFID Avatar answered Oct 08 '22 21:10

Idriss AIT HAFID