Я работаю с реагирующими хуками и машинописью. Я использовал useReducer()
для глобального состояния. Действие функции редуктора содержит два свойства name
и data
. name
означает название события или изменения, а data
будет определенными данными, необходимыми для этого конкретного имени.
До сих пор существует четыре значения имени. Если имя "setUserData"
, то data
должно IUserData
(интерфейс). Если имя setDialog
, то data
должно DialogNames
(тип, содержащий две строки). И если это что-то другое, то данные не требуются.
//different names of dialog.
export type DialogNames = "RegisterFormDialog" | "LoginFormDialog" | "";
//type for name property in action object
type GlobalStateActionNames =
| "startLoading"
| "stopLoading"
| "setUserData"
| "setDialog";
//interface for main global state object.
export interface IGlobalState {
loading: boolean;
userData: IUserData;
dialog: DialogNames;
}
interface IUserData {
loggedIn: boolean;
name: string;
}
//The initial global state
export const initialGlobalState: IGlobalState = {
loading: false,
userData: { loggedIn: false, name: "" },
dialog: ""
};
//The reducer function which is used in `App` component.
export const GlobalStateReducer = (
state: IGlobalState,
{ name, data }: IGlobalStateAction
): IGlobalState => {
switch (name) {
case "startLoading":
return { ...state, loading: true };
case "stopLoading":
return { ...state, loading: false };
case "setUserData":
return { ...state, userData: { ...state.userData, ...data } };
case "setDialog":
return { ...state, dialog: data };
default:
return state;
}
};
//The interface object which is passed from GlobalContext.Provider as "value"
export interface GlobalContextState {
globalState: IGlobalState;
dispatchGlobal: React.Dispatch<IGlobalStateAction<GlobalStateActionNames>>;
}
//intital state which is passed to `createContext`
export const initialGlobalContextState: GlobalContextState = {
globalState: initialGlobalState,
dispatchGlobal: function(){}
};
//The main function which set the type of data based on the generic type passed.
export interface IGlobalStateAction<
N extends GlobalStateActionNames = GlobalStateActionNames
> {
data?: N extends "setUserData"
? IUserData
: N extends "setDialog"
? DialogNames
: any;
name: N;
}
export const GlobalContext = React.createContext(initialGlobalContextState);
Мой <App>
компонент выглядит так.
const App: React.SFC = () => {
const [globalState, dispatch] = React.useReducer(
GlobalStateReducer,
initialGlobalState
);
return (
<GlobalContext.Provider
value={{
globalState,
dispatchGlobal: dispatch
}}
>
<Child></Child>
</GlobalContext.Provider>
);
};
Приведенный выше подход хорош. Я должен использовать его, как показано ниже, в <Child>
dispatchGlobal({
name: "setUserData",
data: { loggedIn: false }
} as IGlobalStateAction<"setUserData">);
Проблема в подходе выше состоит в том, что он делает код немного длиннее. И вторая проблема заключается в том, что я должен импортировать IGlobalStateAction
без причины, по которой мне когда-либо приходилось использовать dispatchGlobal
Есть ли способ, которым я мог бы только сказать, name
и data
автоматически назначается для исправления типа или любой другой лучший способ. Пожалуйста, проведите к правильному пути.