Is it a good idea to store components in state?

Viewed 5408

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?

2 Answers

You ask: "Is it a good idea to store components in state? Is it good practice to store whole React Components in the component state or redux state? [...] but in some cases, it is simpler to just store the component in the state. Is this a bad practice?"

No, it's not a good idea. No, it's not good practice. No, that is never simpler. Yes, it's a bad practice, decidedly.

React is based upon one simple yet powerful core concept: a function receives some values (the properties) and returns the elements that are to be rendered as the immediate result of the current set of values. (All other concepts are only auxiliary: state, side-effects, lifecylce, synthetic events, refs, memoization, tachyon emitters and numberwang)

Storing elements for later use directly breaks this core concept. You risk having stale elements that do not correspond to the current set of properties. (And even if your elements do not have any properties as input you will not gain anything useful by putting them in the state, read on.)

And what is even the intended use of creating react elements and putting them in the state? What perceived problem do want to solve? I can make out two things that you probably try to achieve by doing so: 1. having a means to conditionally render an element; and 2. somehow optimizing the number of times a new react element is created. For both these aims the modus operandi of putting react elements into the state is ill fitted.

For both these aims React provides straightforward solutions: Conditionally rendered elements are achieved thus:

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

or by using a prop

const MyComponent = ({showPrompt}) => {
// ...
<div>
  { showPrompt && <ConfirmationPrompt /> }
</div>

This is simple, it is clear, it is correct. Nothing breaks. No future maintainer is compelled to curse you or poison your vanilla chai latte.

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. (You should employ both mechanisms only when you can actually measure any difference.)

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

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.

Related