-
Notifications
You must be signed in to change notification settings - Fork 5
React Frontend Application
Clément Creusat edited this page Jul 16, 2024
·
3 revisions
Inside each new application made with edifice-react-boilerplate
, you have access to a basic rights
store with zustand
import { RightRole } from 'edifice-ts-client';
import { createStore, useStore } from 'zustand';
type UserRights = Record<RightRole, boolean>;
/**
* https://doichevkostia.dev/blog/authentication-store-with-zustand/
*/
/**
* Basic store for managing "rights" array
* Use this store with `checkUserRight` utils
* You can check rights in a react-router loader
* And set userRights with the store to get a stable global state
*
* const userRights = await checkUserRight(rights);
const { setUserRights } = useUserRightsStore.getState();
setUserRights(userRights);
*/
interface State {
userRights: UserRights;
}
type Action = {
actions: {
setUserRights: (userRights: UserRights) => void;
};
};
type ExtractState<S> = S extends {
getState: () => infer T;
}
? T
: never;
type Params<U> = Parameters<typeof useStore<typeof store, U>>;
const initialState = {
userRights: {
creator: false,
contrib: false,
manager: false,
read: false,
},
};
const store = createStore<State & Action>()((set, get) => ({
...initialState,
actions: {
setUserRights: (userRights: UserRights) => set(() => ({ userRights })),
},
}));
// Selectors
const userRights = (state: ExtractState<typeof store>) => state.userRights;
const actionsSelector = (state: ExtractState<typeof store>) => state.actions;
// Getters
export const getUserRights = () => userRights(store.getState());
export const getUserRightsActions = () => actionsSelector(store.getState());
// React Store
function useUserRightsStore<U>(selector: Params<U>[1]) {
return useStore(store, selector);
}
// Hooks
export const useUserRights = () => useUserRightsStore(userRights);
export const useUserRightsActions = () => useUserRightsStore(actionsSelector);
Best way to check rights is inside a react-router's loader:
- First, you need to fetch a resource to have access to
rights
array - Then, you can pass this data to the
checkUserRight
utility - Finally, send
userRights
to the store
export const loader = (queryClient: QueryClient) => async ({params, request}): LoaderFunctionArgs) => {
// Fetch resource rights
const app = await queryClient.ensureQueryData(queryOptions(params.id));
const userRights = await checkUserRight(app.rights);
const { setUserRights } = getUserRightsActions();
setUserRights(userRights);
}
Then you can use the store in any component or re-export via a custom hook to add other methods that manipulate rights.
- Direct access
const userRights = useUserRights();
- Through a custom hook
export const useAccessStore = () => {
const { user } = useUser();
const userRights = useUserRights();
const hasRightToDoSomething = () => {
const right = userRights.creator ||
userRights.manager
return right;
};
const hasRightToDoSomethingElse = (item: value) => {
const right = (userRights.creator ||
userRights.manager ||
(userRights.contrib &&
item?.owner?.userId.includes(user?.userId as string))) as boolean;
return right;
};
return { userRights, hasRightToDoSomething, hasRightToDoSomethingElse };
};