Search code examples
javascriptreactjsjsxreact-functional-component

In React, can I define a functional component within the body of another functional component?


I've started seeing some of my team writing the following code which made me question if we're doing things the right way as I hadn't seen it written like this before.

import * as React from "react";
import "./styles.css";

function UsualExample() {
  return <h1>This is the standard example…</h1>
}

export default function App() {
  const CustomComponent = (): JSX.Element => <h1>But can I do this?</h1>
  return (
    <div className="App">
      <UsualExample />
      <CustomComponent />
    </div>
  );
}

It seems to render fine and I can't see any immediate adverse effects but is there some fundamental reason why we shouldn't be defining CustomComponent functional component from within another component?

CodeSandbox Example: https://codesandbox.io/s/dreamy-mestorf-6lvtd?file=/src/App.tsx:0-342


Solution

  • This is not a good idea. Every time App rerenders it will make a brand new definition for CustomComponent. It has the same functionality, but since it's a different reference, react will need to unmount the old one and remount the new one. So you will be forcing react to do extra work on every render, and you'll also be resetting any state inside CustomComponent.

    Instead, components should be declared on their own, not inside of rendering, so that they are created just once and then reused. If necessary, you can have the component accept props to customize its behavior:

    const CustomComponent = (): JSX.Element => <h1>But can I do this?</h1>
    
    export default function App() {
      return (
        <div className="App">
          <UsualExample />
          <CustomComponent />
        </div>
      );
    }
    

    On occasion, you may be doing something repetitive inside a single component and want to simplify the code by having a helper function. That's ok, but then you'll need to call it as a function, not render it as a component.

    export default function App() {
      const customCode = (): JSX.Element => <h1>But can I do this?</h1>
      return (
        <div className="App">
          {customCode()}
          <UsualExample />
          {customCode()}
        </div>
      );
    }
    

    With this approach, react will be comparing an <h1> with an <h1>, and so it does not need to remount it.