Skip to content

Instantly share code, notes, and snippets.

@wintondeshong
Last active December 1, 2020 11:33
Show Gist options
  • Save wintondeshong/1d052ade01c8323389aac9b1e67da16b to your computer and use it in GitHub Desktop.
Save wintondeshong/1d052ade01c8323389aac9b1e67da16b to your computer and use it in GitHub Desktop.
React Hook Refactoring
// ----------------------------------------------------
// This is the original react hook before refactoring
// ----------------------------------------------------
import IdentityRecord from "models/view-models/identity-record";
import UserLoginRecord from "models/view-models/user-login-record";
import { useCallback } from "react";
import RoleService from "utilities/services/role-service";
import UserRoleService from "utilities/services/users/user-roles/user-role-service";
import UserService from "utilities/services/user-service";
import UserLoginService from "utilities/services/user-logins/user-login-service";
/**
* Custom hook providing utility functions for the current login of the user
*/
export default function useIdentity() {
const { get: getUserApi } = UserService.useGet();
const { get: getUserLoginApi } = UserLoginService.useGet();
const { list: listUserRolesApi } = UserRoleService.useList();
const { list: listRolesApi } = RoleService.useList();
/**
* Build the identity record including the current User, UserLogin and UserRoles
*
* @param {UserLoginRecord|undefined} (userLogin
* @returns Promise
*/
const buildCurrentIdentity = useCallback(
(userLogin: UserLoginRecord | undefined) => {
const getIdentity = async (
userLogin: UserLoginRecord
): Promise<IdentityRecord | undefined> => {
const userLoginResponse = await getUserLoginApi({
id: userLogin.id!,
});
const userResponse = await getUserApi({
id: userLogin.userId!,
});
const rolesResponse = await listRolesApi();
const userRolesResponse = await listUserRolesApi({
userId: userLogin.userId!,
});
if (userResponse.result!.hasErrors()) {
return;
}
const updatedUserLogin = userLoginResponse.resultObject;
const user = userResponse.resultObject;
const roles = rolesResponse.resultObjects;
const userRoles = userRolesResponse.resultObjects;
if (roles == null || userRoles == null) {
return new IdentityRecord({ user });
}
const userLoginWithRole = updatedUserLogin
?.withRole(roles)
.withUserRole(userRoles);
const identityRecord = new IdentityRecord({
role: userLoginWithRole.role,
user,
});
return identityRecord;
};
if (userLogin == null) {
return;
}
return getIdentity(userLogin);
},
[getUserApi, getUserLoginApi, listRolesApi, listUserRolesApi]
);
return { buildCurrentLogin: buildCurrentIdentity };
}
// ----------------------------------------------------
// First pass refactoring breaking out small functions
// that are single purpose in an effort to reduce
// duplication and hopefully extract out of the hooks
// scope and perhaps out of the file all together.
// ----------------------------------------------------
import IdentityRecord from "models/view-models/identity-record";
import { useCallback } from "react";
import UserLoginRecord from "models/view-models/user-login-record";
import RoleService from "utilities/services/role-service";
import UserService from "utilities/services/user-service";
import UserRecord from "models/view-models/user-record";
import RoleRecord from "models/view-models/role-record";
/**
* Custom hook providing utility functions for the current identity of the user
*/
export default function useIdentity() {
const { get: getRoleApi } = RoleService.useGet();
const { get: getUserApi } = UserService.useGet();
/**
* Build the identity record including the current User and Role
*
* @param {UserLoginRecord|undefined} (userLogin
* @returns Promise
*/
const getHook = (userLogin: UserLoginRecord | undefined) => {
if (userLogin == null) {
return;
}
return getIdentity(userLogin);
};
const getIdentity = async (
userLogin: UserLoginRecord
): Promise<IdentityRecord | undefined> => {
const user = await getUser(userLogin?.userId);
if (user == null) {
return;
}
const role = await getRole(userLogin?.roleId);
return new IdentityRecord({ role, user });
};
const getRole = async (
roleId: number | undefined
): Promise<RoleRecord | undefined> => {
if (roleId == null) {
return;
}
return (await getRoleApi({ id: roleId })).result?.resultObject;
};
const getUser = async (
userId: number | undefined
): Promise<UserRecord | undefined> => {
if (userId == null) {
return;
}
return (await getUserApi({ id: userId })).result?.resultObject;
};
return { getIdentity: useCallback(getHook, [getRoleApi, getUserApi]) };
}
// ----------------------------------------------------
// Second pass refactoring extracting functions out
// of the scope of the hook and into form that could
// easily be pulled into shared utiltiies
// ----------------------------------------------------
import IdentityRecord from "models/view-models/identity-record";
import { useCallback } from "react";
import UserLoginRecord from "models/view-models/user-login-record";
import RoleService from "utilities/services/role-service";
import UserService from "utilities/services/user-service";
import { ServiceResponse } from "andculturecode-javascript-core";
/**
* Loads and constructs data comprising an Identity record
*/
export default function useIdentity() {
const { get: getRoleApi } = RoleService.useGet();
const { get: getUserApi } = UserService.useGet();
/**
* Build the identity record including the current User and Role
*
* @param {UserLoginRecord|undefined} userLogin
* @returns Promise
*/
const getIdentity = async (
userLogin: UserLoginRecord
): Promise<IdentityRecord | undefined> => {
const user = await _call(getUserApi, userLogin.userId);
if (user == null) {
return;
}
const role = await _call(getRoleApi, userLogin.roleId);
return new IdentityRecord({ role, user });
};
/**
* Hook function (a constructor of sorts, but for hooks)
* @param userLogin user login for which to construct the identity
*/
const hook = (userLogin: UserLoginRecord | undefined) =>
userLogin != null ? getIdentity(userLogin) : null;
return { getIdentity: useCallback(hook, [getRoleApi, getUserApi]) };
}
// -----------------------------------------------------------------------------------------
// #region Private Functions
// -----------------------------------------------------------------------------------------
type CallbackOptions = {
id: number;
};
/**
* Calls the provided callback if the id is set
* @param id id value to attempt to load
* @param callback
*/
const _call = async <T>(
callback: (options: CallbackOptions) => Promise<ServiceResponse<T>>,
id: number | undefined
): Promise<T | undefined> => {
if (id == null || id < 1) {
return;
}
return (await callback({ id })).result?.resultObject;
};
// #endregion Private Functions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment