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>
)
}
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>
)
}
Share
Improve this question
asked Sep 17, 2018 at 16:21
corasancorasan
2,7544 gold badges27 silver badges41 bronze badges
4
- 4 I believe so. Usually in the render function you only put some calculated variables to reduce code in your JSX. – Phiter Commented Sep 17, 2018 at 16:23
- 10 I don't think we should do that. By doing this, every time a state updates the function gets redefined. – AKAP Commented Sep 17, 2018 at 16:25
- @ArpitKapadia yeah that makes sense, thanks! – corasan Commented Sep 17, 2018 at 16:35
- 1 …and I'm pretty sure react won't notice that it's still the same component. – Bergi Commented Sep 17, 2018 at 16:43
2 Answers
Reset to default 26Yes, 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 = (props) => (
<ul>
{props.list.map(({ something, thing }) => (
<li className={props.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.
I think in general people avoid defining functions in render but according to this blog post it is not neccesarily a bad practice. The blog post focuses on inline event handler functions being defined in render but I would guess it applies to any function defined in render. Defining functions in render means there is the overhead of redfining them each time render is called but that may not make a noticible performance difference depending on your component.
For the particular example you gave I would reccomend not to define another react component in render. If you do define any functions in render they should be cohesive to what render is doing. Defining another component or adding a bunch of functions inside of render can make in unwieldy and hard to understand what the code is doing.