Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
164 views
in Technique[技术] by (71.8m points)

javascript - 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>
  )
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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's some common patterns to 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 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 you want.

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...