0
\$\begingroup\$

I defined the following context provider:

import { Context, createContext, FC, useContext, useEffect, useState } from "react";
import { element, list, browse } from "../Components/Controls/Controls";

export interface ControlsState {
  shownTab: "Durchsuchen" | "Element" | "Liste";
  listActionTitle?: string;
  listActions: JSX.Element[];
  addListElement?: () => void;

  showTab: (menu: "Durchsuchen" | "Element" | "Liste") => void;
  showListActions: (title: string, actions: JSX.Element[]) => void;
  hideListActions: () => void;
  setAddListElement: (f?: () => void) => void;
}

const initialValue: ControlsState = {
  shownTab: browse,
  listActions: [],

  showTab: (_: "Durchsuchen" | "Element" | "Liste") => {},
  showListActions: (_: string, __: JSX.Element[]) => {},
  hideListActions: () => {},
  setAddListElement: (_?: () => void) => {},
};

const ControlsContext: Context<ControlsState> = createContext<ControlsState>(initialValue);

export const useControlsContext = () => useContext(ControlsContext);

const ControlsContextProvider: FC = ({ children }) => {
  const [state, setState] = useState<ControlsState>(initialValue);

  const context: ControlsState = {
    ...state,
    showTab: (tab: "Durchsuchen" | "Element" | "Liste") => {
      setState((prevState) => {
        return {
          ...prevState,
          shownTab: tab,
        };
      });
    },
    showListActions: (title: string, actions: JSX.Element[]) => {
      setState((prevState) => {
        return {
          ...prevState,
          shownTab: list,
          listActionTitle: title,
          listActions: actions,
        };
      });
    },
    hideListActions: () => {
      setState((prevState) => {
        return {
          ...prevState,
          shownTab: element,
          listActionTitle: "",
          listActions: [],
        };
      });
    },
    setAddListElement: (f?: () => void) => {
      setState((prevState) => {
        return {
          ...prevState,
          addListElement: f,
        };
      });
    },
  };

  useEffect(() => {
    setState((prevstate) => {
      return {
        ...prevstate,
        ...context,
      };
    });
  }, []);

  return <ControlsContext.Provider value={state}>{children}</ControlsContext.Provider>;
};

export default ControlsContextProvider;

Coming from an OOP developing, I don't really like all the let's say public properties. I refactored it followings:

import { Context, createContext, FC, useContext, useEffect, useState } from "react";
import { element, list, browse } from "../Components/Controls/Controls";

interface ControlsInternalState {
  shownTab: "Durchsuchen" | "Element" | "Liste";
  listActionTitle: string;
  listActions: JSX.Element[];
  addListElement?: () => void;
}

export interface ControlsState {
  setTab: (menu: "Durchsuchen" | "Element" | "Liste") => void;
  getTab: () => string;
  setListActions: (title: string, actions: JSX.Element[]) => void;
  getListActions: () => JSX.Element[];
  getListActionsTitle: () => string;
  hideListActions: () => void;
  setAddListElement: (f?: () => void) => void;
  getAddListElement: () => (() => void) | undefined;
}

const initialState: ControlsState = {
  setTab: (_: "Durchsuchen" | "Element" | "Liste") => {},
  getTab: () => browse,
  setListActions: (_: string, __: JSX.Element[]) => {},
  getListActions: () => [],
  getListActionsTitle: () => browse,
  hideListActions: () => {},
  setAddListElement: (_?: () => void) => {},
  getAddListElement: () => undefined,
};

const ControlsContext: Context<ControlsState> = createContext<ControlsState>(initialState);

export const useControlsContext = () => useContext(ControlsContext);

const ControlsContextProvider: FC = ({ children }) => {
  const [exposedState, setExposedState] = useState<ControlsState>(initialState);
  const [internalState, setInternalState] = useState<ControlsInternalState>({
    shownTab: browse,
    listActionTitle: "",
    listActions: [],
  });

  const state: ControlsState = {
    ...exposedState,
    setTab: (tab: "Durchsuchen" | "Element" | "Liste") => {
      setInternalState((prevState) => {
        return {
          ...prevState,
          shownTab: tab,
        };
      });
    },
    getTab: () => internalState.shownTab,
    setListActions: (listActionTitle: string, listActions: JSX.Element[]) => {
      setInternalState((prevState) => {
        return {
          ...prevState,
          listActionTitle: listActionTitle,
          listActions: listActions,
          shownTab: list,
        };
      });
    },
    getListActions: () => internalState.listActions,
    getListActionsTitle: () => internalState.listActionTitle,
    hideListActions: () => {
      setInternalState((prevState) => {
        return {
          ...prevState,
          listActionTitle: "",
          listActions: [],
          shownTab: element,
        };
      });
    },
    setAddListElement: (f?: () => void) => {
      setInternalState((prevState) => {
        return {
          ...prevState,
          addListElement: f,
        };
      });
    },
    getAddListElement: () => internalState.addListElement,
  };

  useEffect(() => {
    setExposedState((prevState) => {
      return {
        ...prevState,
        ...state,
      };
    });
  }, []);

  return <ControlsContext.Provider value={state}>{children}</ControlsContext.Provider>;
};

export default ControlsContextProvider;

I actually defined two different states, one exposed with getter and setters, the second as an internal one, just for the values. Doing so I remove the direct access to the values improving encapsulation and avoiding somebody changes them in a unproper way (mutation).
Are there some cons?

\$\endgroup\$
3
  • \$\begingroup\$ doing OOP in react is already starting you on the wrong path, considering react was built with functional programming at its core. you should instead separate your state by variables and let react handle the state changes for you. ( [tab, setTab] = useState(), etc) for each of your variables and expose them separately \$\endgroup\$ Sep 20, 2021 at 10:04
  • \$\begingroup\$ Well, i know what functional programming is and how reacts state is handled, and I think i am doing it in the correct way. My idea is just to hide properties... \$\endgroup\$
    – Emaborsa
    Sep 20, 2021 at 10:29
  • \$\begingroup\$ then why ask for a code review? \$\endgroup\$ Sep 20, 2021 at 10:34

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.