12

Suppose I define a simple React functional component like this:

const Greeter1 = ({name}) => <h1>Hello {name}</h1>;

I can also define an equivalent plain-old JS function that returns a React element, like this:

const Greeter2 = name => <h1>Hello {name}</h1>;

The "React" version is of course also just a normal JS function, taking in a JS object instead of a string. We could use either of these functions within plain JS to get the greeter element for a given name, just with slightly different caller syntax:

const greeterElement1 = Greeter1({ name: "Bob" });
const greeterElement2 = Greeter2("Bob");

Within a React expression though, we can call these functions in a few different ways:

const someReact1 = <div>{ Greeter2("Bob") }</div>;
const someReact2 = <div>{ Greeter1({ name: "Bob" }) }</div>;
const someReact3 = <div><Greeter1 name="Bob" /></div>;

My question: Are there any effective differences between these calling styles other than syntax aesthetics? I assume someReact1 and someReact2 are virtually identical, but I'm not sure about someReact3. Does using the React component syntax change the way React treats things? Does it affect behavior or performance in any way? Or is it merely syntactic sugar?

When doing a diff on the virtual DOM tree, does React forgo comparing within a functional component if its attributes haven't changed between renderings? And if so, am I correct to assume that that optimization would be lost when calling functions directly as in someReact1?

I want to know b/c in some cases I actually prefer the style of someReact1 as it allows me to use functional programming techniques like currying more easily, plus sometimes it's nice to not have to specify parameter names when calling. But am I paying some kind of penalty by doing so? Am I better off sticking with the traditional syntax?

funlambda
  • 275
  • 2
  • 8
  • I'm in the process of thinking more about this myself. So far, I've figured out that `` basically compiles to `React.createElement(Greeter1)` whereas the function calls embedded in JSX like `{Greeter1()}` do not really change when compiled. That's as far as I've gotten, but that's the real difference, I think. What effect that has on things I haven't yet figured out. They both render identical in the HTML tree. Not sure what they look like in React's virtual DOM. – J.D. Sandifer Jul 18 '19 at 05:51
  • There can also be unexpected differences in the way that things behave as a result of differences in the virtual DOM. This caused a layout-breaking CSS bug for me today: https://stackoverflow.com/questions/67242769/react-material-ui-gridlisttile-looses-style-attributes-when-called-from-within/67242931#67242931 – PeterT Apr 24 '21 at 13:55

2 Answers2

3

Component functions are (a bit?) faster than class components since React 16. Not sure if it's true for prior versions.

Calling component function as a function is much faster than calling it via JSX/React.createElement. But, in most cases, this should not affect the way we write our code since JSX is pretty readable. To fill this gap we should use @babel/plugin-transform-react-inline-elements.

However, I understand your passion to call them as functions. For the sake of composition, I also find it quite useful. This is even more true for plain functions.
But, again, in the future, functional components are going to have a state. If it happens, we may regret our passion :)

Plain functions seem to be faster than component functions. However, I have not digged this topic yet. For example, I'm unsure if it works OK with react-hot-reload. Or, its pros/cons for performance and possible optimizations.


IMO, no matter the differences and in any case, optimizations are still on us - be it a shouldComponentUpdate, or memoization, or keys. Components which might not need any optimizations should be cheap to re-render, and vice versa (if it is a pure calculation, not DOM operation, obviously).

Feel free to comment or correct, I'll update my post.

amankkg
  • 4,503
  • 1
  • 19
  • 30
0

I found an explanation, apparently someone asked a similar question in a Github issue on the official React repository. Here's a link to the issue https://github.com/facebook/react/issues/16796 and an excerpt from the thread, an answer provided by a contributor:

What I want to understand is this: Is it actually necessary to call React.createElement again, since it is already in the return value of the component? Is there any effective difference between that and calling the function component directly, like this?

As @hamlim mentioned, the effective difference is that simply calling a component as a function wouldn't work if the component was more complex than a pure function that returned another React element. React needs the element returned from React.createElement to handle those features.

In the HelloMessage example you could technically just call it as a function, which would be equivalent to doing:

ReactDOM.render(
  <div>Hello Taylor</div>,
  document.getElementById("hello-example")
);

This just inlines the result of HelloMessage, which would render the same UI in the browser. Thats effectively what you're doing by calling HelloMessage as a function. Internally this would be different to React since there's no component mounted, but practically speaking the behavior is identical in this trivial example.

For the ParentComponent example, the same constraints apply. You could call the ChildComponent components as functions if they were just simple components without state or effects, and that would be the same result as just inlining their content into ParentComponent. In same cases this might be what you want, but typically not. If someone added state or effects to one of the ChildComponents, or wrapped it in React.memo or React.lazy, this would break. So use this pattern with caution.

Rinwa
  • 11
  • 1
  • 2