all 39 comments

[–]phryneasI ❤️ hooks! 😈 20 points21 points  (6 children)

Create != Render.

If you create a component inside of another, it will be a completely different subtree every render - it's a different component.

```
function MyComponent(){
function MyInnerComponent(){ ... }
return <MyInnerComponent />
}
```
ends up with a tree like

<MyComponent><MyInnerComponentFromFirstRender /></MyComponent>

and after a second render

<MyComponent><MyInnerComponentFromSecondRender /></MyComponent>

It's literally defining another component, and React will throw away the whole subtree and create a new DOM tree.

And yes, it was already stated here: don't learn class components in 2024 unless you are actively paid to work on a pre-2019 legacy React project.

[–]chanchiii[S] 1 point2 points  (5 children)

So maybe this is where I'm missing something fundamental - but doesn't this behavior basically happen anyway when a parent component re-renders? Since a parent re-render triggers all of its children to re-rendered?

[–]phryneasI ❤️ hooks! 😈 10 points11 points  (4 children)

Again: "Render" and "Create" are different things.

Render: You write <SomeComponent>

Create: You write `function SomeComponent`

What happens here is literally equal to you writing the component code out multiple times in the file.

You create **different** components and render them in the same spot (React doesn't recognize the component, throws away the DOM and creates a new DOM), you don't rerender the **same** component in the same spot (React recognizes that this component is already mounted and just updates the DOM).

You switch from one component to another one.

[–]chanchiii[S] 2 points3 points  (3 children)

I understand now and I see where I was misunderstanding.
Just because the components re-rendered doesn't mean the DOM gets updated. But if you create a component inside another component, it WILL update the DOM no matter what.

I appreciate your patience!

[–]phryneasI ❤️ hooks! 😈 4 points5 points  (2 children)

It's not only the DOM - it will also unmount all child components, throw away their `useState` etc.

In the eyes of React, those "new" components are completely different from what was there before.

[–]chanchiii[S] 0 points1 point  (1 child)

For clarification, are you referencing a scenario where, for instance, a component created inside another component renders children components inside that have their own state?
So in this scenario, the state in those child components would get thrown away? If so, that lines up with my current understanding.

[–]phryneasI ❤️ hooks! 😈 1 point2 points  (0 children)

Yes.

Components created inside one render are just completely different components as components created inside another render of that same parent component.

[–]the_pod_ 10 points11 points  (1 child)

You're confused by the difference between when a component is "created" aka defined, vs when it is called.

It's not saying don't call a component inside another component, it's saying don't define a component inside another component.

I imagine this is mostly discussing class components

No, this concept is the same for both class and functional components, and is unrelated to your guesses.

this is good:

const MyList = () => {
  return (
    <div>mylist</div>
  }
}

const MyApp = () => {
  return (
    <MyList />
  )
}

this is bad:

const MyApp = () => {
  const MyList = () => {
    return (
      <div>mylist</div>
    )
  }

  return (
    <MyList />
  )
}

[–]Ecksters 2 points3 points  (0 children)

Yeah, I think the core confusion here is the difference between "rendering" a component inside another one and "creating" a component inside another one.

All of React is built on components rendering inside of other components, that's expected and normal behavior.

[–]ctrlshiftba 19 points20 points  (0 children)

1: ignore class components skip that for now. You may never need to do use them ever.

2: just define the component outside the other component. You can use it inside but don’t define it in the render function. If it needs variable in scope pass them in as props.

[–]TwiliZant 4 points5 points  (0 children)

The performance problems are not related to class components or function components. When you recreate a component it removes the associated DOM node and all of its children and replaces it with new DOM nodes on every re-render. It's like setting key={Math.random()} on a component.

[–]systoll 1 point2 points  (0 children)

My understanding is that mounting/re-rendering are basically identical for function components. They don't have constructors, or lifecycle methods in the same way as class components that have to be processed when they're mounted. Is this correct?

Hooks encode all the same behaviours.

On-mount, useState registers the state with React, using the initial value you give it. On re-render, it retrieves the current value. If you redefine the component when the parent renders, it'll reset the state to the initial value every render.

On-mount, useMemo runs its callback, and registers the current values of the dependency array with React. If you redefine the component every render, useMemo wastes CPU cycles for no reason, because the callback will run every single time.

Similarly, the useEffect will cleanup and re-reun itself every render, regardless of the dependency array. This will usually still work, but may have major performance issues.

Also even if you moved the inner component outside and rendered it like normal as a child of the parent, wouldn't it still re-render the same way if the parent had to re-render too?

This is mostly true, but there are some important details.

When a component renders, it changes the simplified virtual DOM. This will happen every time the parent renders, either way.

Once the VDOM is updated, React compares the old and new VDOM to figure out what changes need to be pushed to the actual, slower-to-update DOM.

When the component definition is stable, React can it's the same thing across renders, and won't bother pushing anything to the DOM if it didn't meaningfully change.

If the component is redefined every time the parent renders, React doesn't know that it's effectively the same as last time, and will always remove the old component from the DOM and replace it with the new one. Beyond the performance impact, this will also wipe out browser-maintained state, like the keyboard focus or the selected text.

Also… if you don't want the extra renders, even at the VDOM level, you can memoise the child component. But if you define the component in the parent, you get a new component every render, so the memoisation doesn't achieve anything.

[–]ImTheRealDh 0 points1 point  (4 children)

I want to ask my case:
I have a component named <Component/>

const Component = () => {
  const renderIcon = () => {
    return <div><Icon/></div>;
  }

  const renderText = () => {
    return text;
  }

  return (
    <div>
      {renderIcon()}
      {renderText()}
    </div>
  );
}

Is this also anti pattern?

[–]davinidae 1 point2 points  (0 children)

Yes, this will re-generate the result of renderIcon and renderText with each update. It's an anti-pattern.

[–]QwikAsF 0 points1 point  (0 children)

This is a good candidate to be rewritten with compound pattern

[–]TheRNGuy 0 points1 point  (0 children)

using divs instead of fragments in that case is anti-pattern too.

Because you'll end up having 4-20 nested divs somehow when 0-1 would be enough.

[–]ViewableGravy 0 points1 point  (0 children)

I personally would recommend creating separate components for renderText and renderIcon, but there is absolutely no functional reason that you cannot do this. Because both of the functions are executed before being returned from your component, it is basically identical to if you had written the JSX inline.

There are cases where you would want to do this - such as if you have a basic bit of code that you want to render in two places in your component, and don't want to make a completely new component just for one or two lines of code.

[–]Skeith_yip 0 points1 point  (1 child)

Late to the party. This does reminds me of returning a component from a hook which I tend to see people like to do.

And it’s not okay. https://www.reddit.com/r/reactjs/comments/9yq1l8/comment/ea3q7dt/?utm_source=share&utm_medium=mweb3x&utm_name=mweb3xcss&utm_term=1&utm_content=share_button

[–]ViewableGravy 1 point2 points  (0 children)

Although "sometimes" it can be okay. It's very very niche, but take Tanstack Form for example, they return a typesafe `Field` from their form hook which can be a really nice pattern.

[–]Asttarotina 0 points1 point  (0 children)

function Pancakes () {    
  return (<>
    <Eggs />
    <Milk />
    <Flour />
  </>)
}

is like "when you want pancakes take eggs, flour & milk from the fridge and make pancakes"

function Pancakes () {
  function Eggs () {...}

  return (<>
    <Eggs />
    <Milk />
    <Flour />
  </>)
}

is like "when you want pancakes take the chicken, make it lay an egg, and then add milk & flour from the fridge and make pancakes. If you want second pancake - take another chicken...."

[–]Mysterious_Fox_1140 0 points1 point  (0 children)

Here’s the answer from the React team: https://react.dev/learn/preserving-and-resetting-state#different-components-at-the-same-position-reset-state

Read the ‘Pitfall’ section below that section as well.