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?