Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: jsx in a variable vs a function vs a separate component

For rendering smaller components/jsx within a bigger component, there are multiple approaches that one can follow. For example, consider this:

Method 1:

function BigComponent(props) {
  const renderSmallComponent1 = () => <div>{props.a}</div>;
  const renderSmallComponent2 = () => <div>{props.b}</div>;

  return (
    <div>
      {renderSmallComponent1()}
      {renderSmallComponent2()}
    </div>
  )
}

Method 2:

function BigComponent(props) {
  const smallComponent1 = <div>{props.a}</div>;
  const smallComponent2 = <div>{props.b}</div>;

  return (
    <div>
      {smallComponent1}
      {smallComponent2}
    </div>
  )
}

Method 3:

function SmallComponent1({ a }) {
  return <div>{a}</div>;
}

function SmallComponent2({ b }) {
  return <div>{b}</div>;
}

function BigComponent(props) {
  return (
    <div>
      <SmallComponent1 a={props.a} />
      <SmallComponent2 b={props.b} />
    </div>
  )
}

I am just trying to understand the difference in these 3 in terms of

  • dev experience,
  • how the framework treats them,
  • are there any performance optimizations,
  • are there differences in runtime behaviours in all of these?
  • Is either one better to use in certain scenarios?

These are the things that I understand:

  • in Method 3, all SmallComponent are React components which are rendered in another component, so they would have a component lifecycle, while in method 1 and 2, they are simple jsx, which does not have lifecycle, so they would not be mounted / unmounted as React components
  • in Method 2, we would be eagerly evaluating the JSX as it is directly a variable, while in method 1, it would only be evaluated when the function is called in render. So, in case, we have any conditional rendering, the eager evaluation might just be wasteful.

A few other helpful articles:

  • https://medium.com/missive-app/45-faster-react-functional-components-now-3509a668e69f
  • https://kentcdodds.com/blog/dont-call-a-react-function-component

UPDATE: it seems observation 1 is incorrect as all 3 of them would still be rendered as react components, and hence would have a component lifecycle. So react would mount/unmount them.

UPDATE 2: No, observation 1 is correct, method 1 and 2 are both treated as regular jsx as part of the BigComponent and they are not treated as react component which have a lifecycle.

UPDATE 3: There is another method Method 4:

function BigComponent(props) {
  const SmallComponent1 = () => {
  return <div>{props.a}</div>;
  }
  const SmallComponent2 = () => {
  return <div>{props.b}</div>;
  }

  return (
    <div>
      <SmallComponent1 />
      <SmallComponent2 />
    </div>
  )
}

this is similar to Method 3, but Method 3 vs Method 4 is slightly different in execution, when debugging through dev tools.

like image 395
gaurav5430 Avatar asked Apr 20 '21 19:04

gaurav5430


People also ask

Can you store JSX in a variable?

Using JSX, you can create a function and return a set of JSX elements to a variable, and that variable used is to render the elements inside the render() function in React.

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.

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.

Should each React component be a separate file?

All components should be inside components directory Keep each component in a separate file. If you need to have styles etc. for the component then create a folder for the component.


2 Answers

Method 2:

function BigComponent(props) {
  const smallComponent1 = <div>{props.a}</div>;
  const smallComponent2 = <div>{props.b}</div>;

  return (
    <div>
      {smallComponent1}
      {smallComponent2}
    </div>
  )
}
  • If you want to a large UI into seperate smaller UI, this method will give you best performance because
    • It is still just one big UI component.
    • react just have to solve variable references.
    • while re-rendering, BigComponent,smallComponent1 and smallComponent2 are rendered together as single unit.
    • smallComponent1 and smallComponent2 cannot have their own state, life cycles and hooks.
    • smallComponent1 and 2 needs to be re-initialized everytime Bigcomponent state is changed. So it is good practise to wrap them with useMemo() if the result of those smallComponents are coming from an expensive computation.

Method 3:

function SmallComponent1({ a }) {
  return <div>{a}</div>;
}

function SmallComponent2({ b }) {
  return <div>{b}</div>;
}

function BigComponent(props) {
  return (
    <div>
      <SmallComponent1 a={props.a} />
      <SmallComponent2 b={props.b} />
    </div>
  )
}
  • React needs to resolve reference as well as execute the function after resolving the reference.

  • It is a composition of react's actual child components into a large Component.

  • Child components are allowed to have their own hooks.

  • Child components are not re-initialized but are re-rendered if BigComponent state is changed.

  • There is chance of SmallComponent1 and SmallComponent2 getting re-rendered multiple times on BigComponents rendering once if small components are updating thier own state based on props change in parents.

  • if each SmallComponents are supposed to use multiple props which state of BigComponents, Keeping SmallComponents outside BigComponent does offer good developer experience.

  • I hope Method 1 and Method 4 can also be understood using these above points.

  • Note: childcomponents stored in variable and childcompoents as function becomes tricker if your application logic is using ref or DOM element for maininting focus or anchor point of rendering.

like image 133
Firoj Siddiki Avatar answered Oct 22 '22 16:10

Firoj Siddiki


Have you taken a look at the compiled JS in a React project?

JSX tags are essentially transformed in to React.createElement statements. You can read the docs here. Essentially the syntax is:

React.createElement(FunctionOrClassComponent, { props }, ...children)

In all three of your examples this would take place. In all three examples, the smaller components are functional components rather than class components. That is to say, they don't have the React lifecycle methods of a class component, but they can use equivalent React hooks - should you want to.

Edited: Evaluation (instantiation and rendering) depends on your render logic. If you have conditional rendering statements or your functions return null (or less content) based on certain conditions, then obviously you're doing less work. And as you rightly pointed out in the comments below, when you assign a JSX.Element to a variable, that is evaluated inline rather than as a result of a function - so that happens immediately.

To me, all three are valid approaches. To address your questions:

  • dev experience,
    • for small components with minimal state, functional components as variables or lambdas are convenient to write and easily read/parsed when revisiting code at a later date. When a component becomes more complex, you may have to reconsider how it's written and perhaps use Class components.
  • how the framework treats them,
    • to my knowledge the framework treats all three of your examples the same in terms of compilation. I'm unsure about rendering optimisation.
  • are there any performance optimizations,
    • your examples don't depict anything computationally onerous so performance optimization options are not so obvious
  • are there differences in runtime behaviours in all of these?
    • they are all translated to React elements, monitored for props changes, and re-rendered if parents re-render (if things like React.memo are not employed) -- there may be differences vs class-based elements, but I would guess that the runtime differences between your three examples are minimal
  • Is either one better to use in certain scenarios?
    • The differences between all three are more a matter of standards or etiquette than functional outcome. As a developer, I would be able to read and understand all three, but working in a team - I would want to see a standard approach.
like image 42
t0mgerman Avatar answered Oct 22 '22 15:10

t0mgerman