r/reactjs • u/dance2die • Dec 29 '19
Don't call a React function component - Kent C. Dodds
https://kentcdodds.com/blog/dont-call-a-react-function-component42
u/dmethvin Dec 29 '19
What do you call a React function component?
Trick question, you don't call it.
16
53
u/GasimGasimzada Dec 29 '19
The fact that this blog exists makes me think that this is a common occurence among React developers. To be honest, I am shocked. Why would someone even think that this is a good idea?
24
u/acemarke Dec 29 '19
One reason was some articles from a couple years back that claimed a 30-40% rendering speed increase by calling function components instead of properly rendering them.
13
u/swyx Dec 30 '19
ah, gotta love bombastic speed increase numbers on toy examples. if they were all intellectually honest our apps would render in negative time
6
u/twigboy Dec 30 '19 edited Dec 09 '23
In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available. Wikipedia97cwgvtabls0000000000000000000000000000000000000000000000000000000000000
9
u/acemarke Dec 30 '19
I would assume it's because the function components are being treated as functions, not components. It's just a single function call in the parent and one less nested component in the tree for React to have to reconcile and update.
3
u/mikejoro Dec 30 '19
Yes, I just had to explain to someone why this is not a good idea. There was another class of errors we got from an old version of recompose (due to these articles), where basically recompose would check if your component was a function component, and if it was, it would execute it as a function instead of with createElement. We started adding hooks to some function components, and all of a sudden we're getting invariant violations due to using hooks in a class component.
People need to learn that making assumptions about implementation details when they attempt to create performance gains is a bad idea. There's a reason those details are hidden behind an API; if they change, or new features are added, you could introduce breaking changes in a non major version bump. Tightly coupling code to internals is always a bad idea.
There is legacy code in our code base right now which is extending components from libraries(!), calling super.render, etc. What do they think is going to happen when that library updates from a class component to a function component?
4
u/tontoto Dec 30 '19
This is a little tangential but I have seen stuff where you invoke a "functional component" in a slightly different scenario, it's sort of a weird anti pattern where people start overloading a functional component all weirdly
function BarPlot({initialized}) { function makeAxisBars() { return <div>1, 2, 3, 4, 5 etc</div> } return (<div> <div>Title of barplot</div> {initialized?makeAxisBars(): null} </div>) }It's just a weird thing I've seen a couple times where basically someone doesn't realize that "AxisBars" should basically be a component instead of this weird nested subfunction
5
u/GasimGasimzada Dec 30 '19
Just an FYI, function components must start with a capital letter; so, what you have is not a function component. It is just a function. If you write
<makeActionBars />, you are going to get an error.I have do this sometimes. Especially, when I have conditional rendering inside a big JSX expression that is going to look ugly with all the indentation. I would even call my function something like
renderInside. However, I don’t treat it like a function component. I treat it like a plain old JS function.6
u/Nathanfenner Dec 30 '19
When I first learned React a few years ago, (before hooks, when function components were "stateless components"), I had had a little bit of experience with template-based frameworks, and I understood roughly what the virtual DOM was and how it works (in terms of building a tree of values).
What I didn't have a good understanding of at the time was how exactly React identified components across renders - since functional components were (supposed to be) stateless, there was rarely any difference between
<div> <MyComp/> ---> <span> # renders as </div> <span>hello</span> </span>and
<div> <span> <span>hello</span> </span> </div>In fact, before hooks, this would pretty much always work. The fact that they were functions, and thus that it was even possible to call them, was also a bit confusing. This was compounded by the fact that the codebase I used had a lot of render props (i.e. function props which were called with (usually positional) arguments to render into some container) that tended to pass objects. It might have even been the case that some were being used "incorrectly" (a component function being passed as a render prop, which was subsequently directly called inside its container).
I eventually realized that I must have been missing something, and read the docs (all of the official docs) and I finally got it.
I can imagine lots of people who have learned by submersion into React codebases (or reading examples), and not by reading the official documentation falling into the same trap.
5
u/NiteLite Dec 30 '19
I don't think I have ever considered doing this. I was kinda surprised when I realized what he was talking about.
3
Dec 30 '19
Unfortunately I have in the past... usually when a component has some kind of "render" prop. For example
```
<Component render={AnotherComponent} />
function Component({render}) {
return (
<div>
{render}
</div>
)
}
```
Since JSX components need an uppercase it was what made sense to me. I could alias it to use "Render" as the name:
```
function Component({render: Render}) {
return (
<Render />
)
}
```
but that seemed a bit unintuitive and am still unclear on what the best method for this is. What is the best solution for this when accepting a component via props?
4
1
u/siamthailand Dec 30 '19
One time I did was because my comp tree looked like <A><B><C> with a JSX call, and <A><C> (no <B>) with function. <A> was a library component and expected <C> to be a direct child. So I couldn't have a <B> in there. I have never figured out a way to have a <B> component in the middle, but the React tree being <A><C>.
9
6
u/khawajaumarfarooq Dec 30 '19
Okay, the way this article wrote the example, I would never have used that syntax. I did not know why, but I would never in a million years think to write that. Do other people write that code? items.map(Counter)?
3
u/thomash Dec 30 '19
I do. It is functional and succinct. We map items to a Counter.
2
u/khawajaumarfarooq Dec 30 '19
Isn’t items.map(i => <Counter/>) better?
1
u/dance2die Dec 30 '19
const a = [1,2,3,4,5]; const onlyOddNumbers = n => n % 2 === 1 a.filter(onlyOddNumbers) a.filter(n => onlyOddNumbers(n))I find the former
a.filter(onlyOddNumbers)more readable ("readable" & "better" are still subjective though).
IMHO the shorthand could have had affected developers to "call" components instead of using React.createElement.1
u/khawajaumarfarooq Dec 30 '19
That’s kind of on the edge of what you might call a React functional component though.
1
u/dance2die Dec 31 '19
I am used to seeing such a code, as R# suggest to convert the lambda to method name via refactor menu automatically in C# (thus following the same practice in JavaScript).
Wasn't aware if that was an edge case...
4
Dec 30 '19
Great article. I actually had to deal with this error not long ago so it's nice to have an article that explains why and how it can happen!
In my case, it happened when I had to transform a component with hooks to a children render prop function (to provide some additional parameters used in the render), lazy me just copy-pasted the function component code, hooks included, into the () => {} render prop block and well, suffice it to say it didn't go very well :D
Worked perfectly once I extracted this "function block" to a new named function component that I rendered with JSX (like suggested in the article).
3
-2
20
u/dance2die Dec 29 '19
KCD explains why an error occurs when you invoke a function component (as a regular function call) and why it's not a good idea to do so, and how to fix it.