Is it a good idea to store components in state?
Asked Answered
C

6

18

Is it good practice to store whole React Components in the component state or redux state? Yes, it's optional as we could store a string in the state and render the component conditionally but in some cases, it is simpler to just store the component in the state.

For example,

const [ components ]  = useState([
    { id: 1, component: <Login />, title: `Login` },
    { id: 2, component: <Register />, title: `Register` },
])

But components can be large and I was wondering if that makes any difference. Is this a bad practice?

Chlorpromazine answered 2/4, 2021 at 12:1 Comment(2)
Sometimes I do that, when in page can be one parent component and I need use different childs components in itUnoccupied
Why not simply write const components = [{ id: 1, component: Login, title: "Login" }, { id: 2, component: Register, title: "Register" }] outside your component?Zygote
B
27

I would not recommend it. But not for the reason that those instances would somehow add too much size to the state (which I don't think they would).

The strongest argument in my opinion is: the instantiated components are stale; their properties will only have those values that were assigned when they were instantiated. That might or might not lead to problems down the road. It definitely introduces an additional risc for bugs in your app. Normally all components are created and parameterized when a render function is run; receiving the newest property values; this will not be the case for those that are kept in state.

Instantiating a component is usually not an expensive operation; and it does not imply a corresponding DOM update, as react uses a diffing mechanism to decide which parts of the DOM needs updating.

All the use cases I can think of -where storing instantiated components in state might be considered- can be solved in a more react idiomatic way:

  • Conditionally rendered elements:

    const [showPrompt, setShowPrompt] = useState(false);
    // ...
    <div>{showPrompt && <ConfirmationPrompt />}</div>;
    

    or

    const MyComponent = ({showPrompt}) => {
    // ...
    <div>
      { showPrompt && <ConfirmationPrompt /> }
    </div>
    
  • Optimizing the number of times a new react element is created:
    If you have reason to think your render function is quite heavy and you want to reduce the number of times it is run, you can use the useMemo hook (to memoize calculation results within you render function) or you can wrap your whole component in React.memo() so it is only re-rendered when the props change.

    const primeNumbers = useMemo(() => calculatePrimeNumbers(limit), [limit]);
    
    const MyComponent = React.memo((props) => {
      /* render using props */
    });
    

My original answer was caustic. I edited it, keeping the reasoning, but removing the unhelpful parts.

Birl answered 4/4, 2021 at 21:50 Comment(5)
Very well explained. Thank you.Chlorpromazine
So one should really avoid libraries like github.com/Quernest/mui-modal-provider which store open modals in state along with their props, in order not to have to add them conditionally wherever they are used ?Decency
Downvoted. Among all this text there's no clear point of why is it a bad practice. Author uses vague terms like "core concept", "breaks this core concept". Makes false conclusion that storing a component in state == storing elements for later use. Tries to guess the need for such pattern but fails to find a good example (see the comment above).Hitherward
I'm storing components in state before rendering, is it bad? codesandbox.io/embed/…Berlyn
The instantiated components are stale - defaultValue and initialValue are reasonbly understood patterns in react. There's no difference between those props and the concept that a component becomes "stale". As long as you are clear about what you're doing, the argument that "components are stale" doesn't make sense.Rodl
N
4

Actually, it works but really it is not a good idea, it is very hard for ReactJS to compare it, right it in state object or modify it or delete it.

Use simple string for your state, store components in static object and then play with them:

const StaticList = {
  Login, // <<== pay attention, I don't use JSX, I pass the imported name
  Register,
};

const YourComponent = () => {
  const [ components ]  = useState([
    { id: 'one', cn: 'Login', title: `Login` },
    { id: 'two', cn: 'Register', title: `Register` },
  ]);


  return (
    <div>
      {components.map(({ id, cn, title }) => {
        const Comp = StaticList[cn];

        return (
          <div key={id}>
            <span>{title}</span>
            <Comp />
          </div>
        );
      })}
    </div>
  );

Something like above, it is a simple sample.

Nietzsche answered 2/4, 2021 at 12:17 Comment(7)
I'm undecided whether to up-vote or down-vote your answer. You are right when you state it is not a good idea but then you continue to provide an alternative that is utterly useless and confusing. Pray tell, what sense would it ever make to store strings in a dictionary to retrieve components in that manner? Why not store the components directly? And why store them at all?Birl
Dear @Martin, Just like I said, storing Component in state is not a good idea because it has heavy cost for ReactJS to compare some stuffs, and if you want to change some Components by some state, so, what is your idea? the simple idea could be having simple string name instead of heavy object. also, I couldn't understand why you don't like this simple way? what is complex?Nietzsche
heavy cost for ReactJS to compare some stuffs - this is extremely vague. From my understanding, react uses referential integrity everywhere for perf optimization, which is insanely cheap. What part is expensive exactly?Rodl
@AdamJenkins, By my understanding, if a developer puts whole a component into a state, comparing would be so expensive. If I'm wrong please give me an update or link, I would appreciateNietzsche
As with all premature optimization problems, the onus is never on me to prove that something is fast, the onus is on the person claiming that something is slow to prove that is actually is. This is react's objectIs that is uses for comparsion: github.com/facebook/react/blob/…. That's a pretty fast check. Here's one place in ReactFiber where that check is performed: github.com/facebook/react/blob/…Rodl
If anything, storing a stateful ReactElement in another elements state could potentially result in a performance improvement because you'll be able to isolate renders more easily - you won't have to render the entire parent when just the stateful component wants to re-render itself. 98% of the time, however, the number of times something renders in react is largely irrelevant to everyoneRodl
Finally found the state update check used internally that used react's object.is - this is the comparison I believe you are talking about: github.com/facebook/react/blob/…Rodl
R
1

Please don't listen to people's thoughts and opinions about performance, measure it yourself

The linked stackblitz shows that there is not a significant performance difference (run the test suite a few times and the benchmark results flip back and forth to which is faster - meaning there's nothing meaningful)

If you want to use an element in state, just obey the fundamental rule of react - don't mutate stuff - and you'll be fine.

You're no more likely to have bugs by storing an element in state than you are by storing anything else.

Rodl answered 14/10, 2023 at 10:56 Comment(1)
Thanks for your awesome answer and for commenting under my answer, you're 100% right, I learned many things in your discussion. Now I'm a little bit busy but I'll edit my answer for sure.Nietzsche
M
0

React suggests that storing components in state is not a good Idea.

Instead:

State should contain data that a component's event handlers may change to trigger a UI update. In real apps this data tends to be very small and JSON-serializable

Milagro answered 26/7 at 9:4 Comment(0)
P
0

Actually this is a good question, even i don't know react will behave so I tested a sample code in stackblitz and this is what i found.

function App() {
const [components] = useState([
{ id: 1, component: <Login />, title: `Login` },
{ id: 2, component: <Register />, title: `Register` },
]);
return (
 <div >
  {components.map((i) => {
    console.log(i)
    return(
      <div key={i?.id}>
      {i?.id}
      {i?.Component}
      {i?.title}
    </div>
    )
    })}
   </div>
  );
}

React is displaying id and title, but not the component, because Component is coming as Object with ref,props,key etc...

ConsoleOutput

Pamplona answered 26/7 at 10:10 Comment(0)
G
-1

This is simple, and it's an anti-pattern, ReactNode/JSX.Element are complex data structures representing the structure of a React component tree, and they contain references to JavaScript functions, component instances, and other non-serializable data. Basically, you shouldn't store non-serializable data in state, you should only store serializable data in your state which are typically plain JavaScript objects or primitives.

With that in mind, even though you can technically store non-serializable data in your state/store, Redux highly recommend that you shouldn't, see here

Gnomic answered 31/8, 2023 at 10:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.