Search
⌘K
    to navigateEnterto select Escto close

    Avoid too many individual state hooks

    The problem

    Too many useState hooks can make your code spaghetti code. It'll be hard to maintain as it keeps growing. Here is an example of spaghetti state management:

    1const Themes = () => {
    2  const [themes, setThemes] = useState({ themes: [], customThemes: [] });
    3  const [isTemplateLoading, setTemplateLoading] = useState(false);
    4  const [isCloneDefaultTheme, setIsCloneDefaultTheme] = useState(false);
    5  const [templateCodeChanged, setTemplateCodeChanged] = useState(false);
    6  const [isAlertOpen, setIsAlertOpen] = useState(false);
    7  const [themeName, setThemeName] = useState("");
    8  const [originalThemeName, setOriginalThemeName] = useState("");
    9  const [currentTab, setCurrentTab] = useState(TABS.themes);
    10  const [configuration, setConfiguration] = useState({});
    11  const [previewUrl, setPreviewUrl] = useState(null);
    12  const [publishedTheme, setPublishedTheme] = useState(null);
    13  const [organizationThemeId, setOrganizationThemeId] = useState(null);
    14  const [savedConfig, setSavedConfig] = useState({});
    15  const [templateContent, setTemplateContent] = useState("");
    16  const [savedTemplateContent, setSavedTemplateContent] = useState("");
    17  const [template, setTemplate] = useState({});
    18  const [templateId, setTemplateId] = useState(null);
    19  const [organisationThemeTemplates, setOrganisationThemeTemplates] = useState(
    20    []
    21  );
    22  const [initialLoad, setInitialLoad] = useState(true);
    23  const [cloneTemplate, setCloneTemplate] = useState(null);
    24  const [groupedTemplates, setGroupedTemplates] = useState({});
    25  const [typographs, setTypographs] = useState([]);
    26  ...

    This would mean that sending the states and state handlers to child components become messy too:

    1  <ThemeSidebar
    2    themes={themes}
    3    template={template}
    4    themeName={themeName}
    5    currentTab={currentTab}
    6    configuration={configuration}
    7    groupedTemplates={groupedTemplates}
    8    isTemplateLoading={isTemplateLoading}
    9    organizationThemeId={organizationThemeId}
    10    typographs={typographs}
    11    setThemeName={setThemeName}
    12    setCurrentTab={setCurrentTab}
    13    setTemplateId={setTemplateId}
    14    ...
    15    setIsAlertOpen={setIsAlertOpen}
    16    setOrganizationThemeId={setOrganizationThemeId}
    17  />

    This wasn't an issue in class components since it was all handled in a single state object and a setState() method.

    The solution

    There are many ways to fix this issue such as using useReducer hook. But the easiest and most familiar way to handle this is to have a state management solution similar to the one in class components. For that we can use the useSetState custom hook. The above code can be simplified to:

    1import {useSetState} from 'react-use';
    2const Themes = () => {
    3    const [state, setState] = useSetState({
    4        isTemplateLoading: false,
    5        isCloneDefaultTheme: false
    6        templateCodeChanged: false,
    7        isAlertOpen: false,
    8        themeName: "",
    9        originalThemeName: "",
    10        currentTab: TABS.themes,
    11        configuration: {},
    12        previewUrl: null,
    13        publishedTheme: null,
    14        organizationThemeId: null,
    15        savedConfig: {},
    16        templateContent: "",
    17        savedTemplateContent: "",
    18        template: {},
    19        templateId: null,
    20        organisationThemeTemplates: [],
    21        initialLoad: true,
    22        cloneTemplate: null,
    23        groupedTemplates: {},
    24        typographs: []
    25    });
    26    ...
    27    setState({ themeName: "My theme" });

    This would make passing states and their handlers to child components much easier:

    1<ThemeSidebar themeStates={state} setThemeStates={setState} />
    Previous
    Next