It`s a template for the fast development of the Front end. React-template use generic components and Material UI library to create UI and React Query for interaction with the API.
To create a new application with React-template, try to clone this repository on your own PC using HTTPS or SSH.
git clone git@github.com:Sendrato/react-template.git
git clone https://github.com/Sendrato/react-template.git
The next step change a remote repository.
git remote rm origin
git remote add <name> <new-repository-link>
yarn install
yarn app:start:dev
yarn app:build
yarn lint
Start the storybook, to view the documentation on UI components and props them receive.
Move into the app directory
cd packages/app
Run Storybook
yarn storybook
Open Storybook locally
http://localhost:6006
React-template uses AuthContext for authorization and already has auth module. Change the design login page if it is necessary and change the endpoints of login, getToken, refreshToken, and getUserRole functions in AuthContext according to your API. For work with AuthContext in components you can use hook useAuthContext. Also, React-template has AuthGuard and RoleGuard components in order to limit access to the pages of users who are not authorized or whose role does not correspond to the role that has access to a certain page.
const authContext = useAuthContext();
The React template uses a Next.js router. To create a new page add a file with a name that will match the route on your website to the pages directory. And add the according to module to the module directory for creating a page template. Also, add info about a page to routes.ts to provide information about the page.
export const HOME_ROUTE = {
pathname: '/',
access: ['organiser', 'administrator', 'seller'],
title: 'Overview',
};
export const routes: IRoute[] = [HOME_ROUTE];
To add a link to a page in a Sidebar you should update sidebardItems.ts in the layouts/Sidebar directory:
import { HOME_ROUTE } from 'routes/routes';
const pagesSection: SidebarItemsType[] = [
{
...HOME_ROUTE,
},
];
const navItems = [
{
title: 'Pages',
pages: pagesSection,
},
];
export default navItems;
React-template uses an approach for data fetching that is based on React Query and custom hook useEntityQuery.
useEntityQuery props:
- entity - the entity string.
- params - the query params of the request.
- deps - the array of parameters after changing which the data should be updated.
- options - the options of useQuery hook from React Query.
Hook returns the object like response of useQuery hook.
Example:
const { data, isFetching, refetch } = useEntityQuery<ListSellers>({
entity: '/common/backoffice/onboarding/ListSellers',
params: `Start=${page * rows}&PageSize=${rows}`,
deps: [page, rows],
});
The hook useEntityMutation serves for creating an instance of POST, DELETE, PUT requests. He returns the object like useMutation hook from React Query.
useEntityMutation props:
- entity - the entity string;
- method - POST, PUT, DELETE.
- options - the options useMutation hook from React Query.
- successMessage - the message that will display like a record after a success request.
- errorMessage -the message that will display after a bad request.
- errorType - the notification type of error message.
Example:
const mutation = useEntityMutation<unknown, SellerDTO>({
entity: '/common/backoffice/onboarding/Seller',
method: METHOD.POST,
});
If you need to perform a certain mutation and then update the data, you can use a combination of both hooks.
Example:
const { refetch } = useEntityQuery<ListSellers>({
entity: '/common/backoffice/onboarding/ListSellers',
params: `Start=${page * rows}&PageSize=${rows}`,
deps: [page * rows, rows],
});
const { mutateAsync } = useEntityMutation<unknown, SellerDTO>({
entity: '/common/backoffice/onboarding/Seller',
method: METHOD.POST,
});
const handleCreateSeller = async (body: SellerDTO) => {
await mutateAsync({ body });
await refetch();
};
The useEntityWebsocket serves for convenient work with WebSockets. The hook gets the object with such arguments as:
- entity - string for SUBSCRIBE and UNSUBSCRIBE on WS.
- handleWsMessage - function for handling messages from WS.
const [messages, setMessages] = useState([]);
const saveWsMessages = (value) => {
setMessages((prev) => [...prev, value]);
};
useEntityWebsoket({
entity: `common/backoffice/chat/Chat?Recipient=${email}`,
handleWsMessage: saveWsMessages,
});
The react-template has a list of custom hooks that help faster develop new features. Common custom hooks:
- usePagination.
- useSort.
- useKey.
- useMedia.
- useEventListener.
And also hooks for work with a table:
- useSelectedRow.
- usePaginationState.
- useSortState.
The usePagination hook returns config for pagination that include such properties:
- page - the current page.
- rows - the current rows count per page.
- handleChangePage - a function for changing the current page,
- handleChangeRows - a function for changing the current rows count,
- setPage - the setState action for setting new page value.
- setRows - the setState action for setting new rows value.
const { page, rows, handleChangePage, handleChangeRows } = usePagination();
The useSort hook return config for sorting data that include such properties:
- sortBy - the property by which to sort
- sortDirection - ASC | DESC,
- setSortBy - the setState action for setting new sortBy value,
- setSortDirection - the setState action for setting new sortDirection value,
const { sortBy, sortDirection, setSortBy, setSortDirection } = useSort();
The useKey hook serves to add listeners to keyboard events and gets properties:
- callback - the function that will perform by clicking on a specific key.
- key - the code of some keyboard item.
useKey(() => console.log('enter'), 'ENTER');
The hook useMedia returns three boolean values: isMobile, isTablet, and isDesktop. One of these values is true depending on the current window width.
const { isMobile, isTablet, isDesktop } = useMedia();
The hook useEventListener adds a listener to some event and removes it after unmounting the component where the listener was added. For using it with some UI elements, you can pass a ref on this element as a third argument.
const buttonRef = useRef<HTMLButtonElement | null>(null)
const onScroll = () => console.log('window scrolled!');
const onClick = (event: Event) => console.log('button clicked!');
useEventListener('scroll', onScroll);
useEventListener('click', onClick, buttonRef)
The hook useSelectedRow returns sortConfig for a Table that includes:
- selected - an array of selected items.
- setSelected - a setState action for updating the array of selected items.
- onSelect - a function that will select one element.
- selectAll - a function that will select all elements.
- selectedKey - the key by which the elements are compared.
Hook useSelectedRow get such arguments:
- data - array with initial data.
- selectedKey - the key by which the elements are compared.
Also for declaring the type of item from returned array better-set item type using Generics.
type TProduct = {
id: string;
price: number;
description: string;
};
const { data } = useEntityQuery(
entity: "common/backoffice/products/ProductsList"
);
const selectedConfig = useSelectedRow<TProduct>(data, 'id');
The useSortState is hook for a data sorting on a client side. He gets an array of initial data and return object with next properties:
- sortItems - an sorted array.
- sortBy - the property by which to sort.
- sortDirection - ASC | DESC,
- setSortBy - the setState action for set new sortBy value.
- setSortDirection - the setState action for set new sortDirection value.
- handleSort - function for sorting data.
const { data } = useEntityQuery(
entity: "common/backoffice/products/ProductsList"
);
const {sortItems, handleSort, sortBy, sortDirection} = useSortState(data);
useEffect(() => {
handleSort(sortBy, sortDirection)
}, [sortBy, sortDirection]);
The usePaginationState is a hook for pagination on the client side. He gets an array of initial data and returns an object with the next properties:
- showData - an array of items on a current page.
- page - the current page.
- rows - the current rows count per page.
- handleChangePage - the function for changing pages,
- handleChangeRows - the function for changing rows,
- setPage - the setState action for setting new page value.
- setRows - the setState action for setting new rows value.
- handlePaginate - the function for pagination data;
const { data } = useEntityQuery(
entity: "common/backoffice/products/ProductsList"
);
const {showData, handlePaginate page, rows} = usePaginationState(data);
useEffect(() => {
handlePaginate(page, rows)
}, [page, rows]);