Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it an anti-pattern to define a function component inside the render() function?

I want to know if it's an anti-pattern or if it affects the component somehow to do something like this:

render() {
  const MyFuncComponent = ({ prop1, prop2 }) => (
    // code here
  )

  return (
    <div>
      <MyFuncComponent prop1={something} prop2={else} />
    </div>
  )
}
like image 508
corasan Avatar asked Sep 17 '18 16:09

corasan


People also ask

Can we use render () inside functional component?

There is no render method used in functional components. With functional components there are fewer lines of code another positive to this is that the fewer lines of code a developer has to read and write, the faster and easier their code is to comprehend.

Can we call function inside render?

In this article, we will learn how to call a function to render in ReactJS. React is having 2 types of components, i.e, Class based and Functional Components, but render method is only in Class based components. So we will make a class based component and call a function in that.

What does render () do in React native?

In React, the render() method is the only required method in a class component and is responsible for describing the view to be rendered to the browser window. Coupled with the clever way React operates around its virtual DOM concept, there are certain subtleties in how this method works.

Why does a component requires a render () method?

render() method It is seen as a normal function but render() function has to return something whether it is null. When the component file is called it calls the render() method by default because that component needs to display the HTML markup or we can say JSX syntax.


1 Answers

Yes, this is an anti-pattern for the same reason we shouldn't use a Higher-Order-Component inside of render.

Don’t Use HOCs Inside the render Method

React’s diffing algorithm (called reconciliation) uses component identity to determine whether it should update the existing subtree or throw it away and mount a new one. If the component returned from render is identical (===) to the component from the previous render, React recursively updates the subtree by diffing it with the new one. If they’re not equal, the previous subtree is unmounted completely.

Normally, you shouldn’t need to think about this. But it matters for HOCs because it means you can’t apply a HOC to a component within the render method of a component:

render() {
  // A new version of EnhancedComponent is created on every render
  // EnhancedComponent1 !== EnhancedComponent2
  const EnhancedComponent = enhance(MyComponent);
  // That causes the entire subtree to unmount/remount each time!
  return <EnhancedComponent />;
}

The problem here isn’t just about performance — remounting a component causes the state of that component and all of its children to be lost.

This means that the new component will appear in the React tree (which can be explored with the react-devtools) but it won't retain any state and the lifecycle methods like componentDidMount, componentWillUnmount, useEffect will always get called each render cycle.

Solutions

Since there are probably reasons for dynamically creating a component, here are some common patterns that avoid the pitfalls.

Define the new component outside

Either in its own file, or directly above the parent component's definition. Pass any variable as props instead of using the parent component's scope to access the values.

const MyFuncComponent = ({ prop1, prop2 }) => <>{/* code here */}</>;

const MyComponent = props => (
  <div>
    {props.list.map(({ something, thing }) => (
      <MyFuncComponent prop1={something} prop2={thing} />
    ))}
  </div>
);

Helper function

A regular function that returns JSX can be defined and used directly inside another component. It won't appear as a new component inside React's tree, only its result will appear, as if it was inlined.

That way, we can also use variables from the enclosing scope (like props.itemClass in the following example) in addition to any other parameters.

const MyComponent = props => {
  // Looks like a component, but only serves as a function.
  const renderItem = ({ prop1, prop2 }) => (
    <li className={props.itemClass}> {/* <-- param from enclosing scope */}
      {prop1} {prop2} {/* other code */}
    </li>
  );

  return <ul>{props.list.map(renderItem)}</ul>;
};

It could also be defined outside the component since it's really flexible.

const renderItem = (itemClass, { prop1, prop2 }) => (
  <li className={itemClass}>
    {prop1} {prop2} {/* other code */}
  </li>
);

const MyComponent = props => (
  <ul>
    {props.list.map(item => renderItem(props.itemClass, item))}
  </ul>
);

But at that point, we should just define a React component instead of faking it with a function. Use React in a predictable manner and to its full potential.

Inline the logic

It's really common to inline JSX inside of a condition or a map callback.

const MyComponent = ({ itemClass }) => (
  <ul>
    {props.list.map(({ something, thing }) => (
      <li className={itemClass}>
        {something} {thing} {/* other code */}
      </li>
    ))}
  </ul>
);

If we find ourselves copy-pasting this same inlined JSX everywhere, it might be time to wrap it up in its own reusable component.

like image 164
Emile Bergeron Avatar answered Nov 14 '22 22:11

Emile Bergeron