Why is it bad practice to store a component as the state value?
const [Component, setComponent] = useState<JSX.Element>(<Empty />);
Say I want to conditionally render a component based off a number of different criteria (all mutually exclusive). But before actually rendering, I would like to add a debouncer (delayed rendering after x
ms of inactivity). I wouldn't necessarily do this as a go-to, but it seems more intuitive and is less code to just assign the component as the state value (in this scenario). I could set up my state to hold a text value, reference that everywhere and set up a map variable to map the string to a component. But it seems unnecessary. I've read online that it's bad practice, and you should only put data in state, but everyone seems to convenient leave out the why it's a bad practice. Nothing in the docs seems to indicate this is bad practice either.
Here's an example that works, hopefully illustrating why setting components in state is convenient. Each of the Message
components are memoized with React.memo
so there's no chance of their props changing:
import React, { useState, useEffect } from 'react';
import useDebounce from '../../hooks/useDebounce';
import {
TooShort,
NoPrompt,
LimitWarning,
LimitReached,
Empty,
} from './Messages';
interface Props {
promptAreaTouched: boolean;
promptText: string;
debounceTimeout?: number;
}
const DEBOUNCE_TIMEOUT = 2000;
const SHORT_PROMPT_LENGTH = 5;
const APPROACHING_PROMPT_LENGTH = 40;
const MAX_PROMPT_LENGTH = 50;
const PromptLengthMessage = ({
promptAreaTouched,
promptText,
debounceTimeout = DEBOUNCE_TIMEOUT,
}: Props) => {
const [Component, setComponent] = useState<JSX.Element>(<Empty />);
const DebouncedComponent = useDebounce(Component, debounceTimeout);
const numWords = promptText.split(/\s+/).length;
const isEmpty = promptAreaTouched && promptText.length === 0;
const isTooShort = promptAreaTouched && numWords <= SHORT_PROMPT_LENGTH;
const limitWarning =
numWords >= APPROACHING_PROMPT_LENGTH && numWords < MAX_PROMPT_LENGTH;
const limitReached = numWords >= MAX_PROMPT_LENGTH;
useEffect(() => {
switch (true) {
case isEmpty:
setComponent(<NoPrompt />);
break;
case isTooShort:
setComponent(<TooShort />);
break;
case limitWarning:
setComponent(<LimitWarning />);
break;
case limitReached:
setComponent(<LimitReached />);
break;
default:
setComponent(<Empty />);
}
}, [promptText]);
return DebouncedComponent === Component ? DebouncedComponent : <Empty />;
};
export default PromptLengthMessage;