This is an old revision of the document!
In acest laborator vom intra in detaliu legat de gestionarea starii aplicatiei folosind de hook-uri si Redux Toolkit. Motivul pentru care este nevoie de gestiunea starii aplicatiei este ca anumite componente in diferite locatii ale aplicatiei au nevoie sa imparta aceleasi date. O posibilitate pentru a propaga datele la mai multe componente este ca o compenta parinte sa trimita la descendentii sai datele prin proprietati din copil in copil, insa aceasta abordare poate aglomera componentele si duce la cod greu de gestionat. Alternativa cea mai buna este ca datele partajate de diferite componente sa fie puse la dispozitie printr-o stare globala accesibila prin functii speciale numite hooks. De asemnea, vom prezenta in acest laborator si modalitati de a crea formulare pentru a executa mutatii pe backend si cum puteti gestiona starea formularului cu validarea datelor introduse.
La laboratoarele precedente a fost prezentata definirea componentelor ca extindere a clasei React.Component si definirea componentelor in mod functional. In cele mai multe cazuri daca componenta are nevoie sa de logica mai complexa este de preferat sa fie definita ca componenta functionala motivul fiind ca logica complexa a componentei poate fi sparta si chiar extrasa prin intermediul de hook-uri.
Un hook, asa cum a fost prezentat in laboratoarele precedente cu exemplu useState, este o functie speciala care este apelata de catre framework in mod automat cand variabilele de care depinde se modifica iar iesirile acestei functii daca se modifica vor declansa recalcularea componentei de catre framework.
Componentele functionale pot apela in interior hook-uri dar si hook-urile la randulor lor pot apela alte hook-uri, practic dezvoltatorul poate sa-si creeze propriile hook-uri particularizate din altele. Hook-urile ajuta ca logica sa unei componente sa fie separata de definirea UI-ului pentru a degreva componenta de anumite responsabilitati cum este gestionarea starii interne.
Mai jos este un exemplu de hook particularizat pentru gestionarea starii paginarii preluat din aplicatia demo a nostra.
/** * This is the pagination controller hook that can be used to manage the state for paged entries. */ export const usePaginationController = () => { const [page, setPage] = useState(1); // Create a state for the current page. const [pageSize, setPageSize] = useState(10); // Create a state for the current page size. const setPagination = useCallback((newPage: number, newPageSize: number) => { // Create a callback to set both the current page and page size. setPage(newPage); setPageSize(newPageSize); }, [setPage, setPageSize]); return { // Return the state and its mutations. page, pageSize, setPage, setPageSize, setPagination } }
Observati ca hook-ul se numeste incepand cu use ca sa se faca distinctia de alte functii in cod. Noi o sa numim hook-urile care contin logica unei componente controller hook. Mai jos e un exemplu de cum se poate folosi hook-ul precedent in altul impreuna cu alte hook-uri.
/** * This is controller hook manages the table state including the pagination and data retrieval from the backend. */ export const useUserTableController = () => { const { getUsers: { key: queryKey, query }, deleteUser: { key: deleteUserKey, mutation: deleteUser } } = useUserApi(); // Use the API hook. const queryClient = useQueryClient(); // Get the query client. const { page, pageSize, setPagination } = usePaginationController(); // Get the pagination state. const { data, isError, isLoading } = useQuery([queryKey, page, pageSize], () => query({ page, pageSize })); // Retrieve the table page from the backend via the query hook. const { mutateAsync: deleteMutation } = useMutation([deleteUserKey], deleteUser); // Use a mutation to remove an entry. const remove = useCallback( (id: string) => deleteMutation(id).then(() => queryClient.invalidateQueries([queryKey])), [queryClient, deleteMutation, queryKey]); // Create the callback to remove an entry. const tryReload = useCallback( () => queryClient.invalidateQueries([queryKey]), [queryClient, queryKey]); // Create a callback to try reloading the data for the table via query invalidation. const tableController = useTableController(setPagination, data?.response?.pageSize); // Adapt the pagination for the table. return { // Return the controller state and actions. ...tableController, tryReload, pagedData: data?.response, isError, isLoading, remove }; }
In final, se poate folosi controller hook-ul in componenta de UI.
export const UserTable = () => { const { userId: ownUserId } = useAppSelector(x => x.profileReducer); const { formatMessage } = useIntl(); const header = useHeader(); const orderMap = header.reduce((acc, e, i) => { return { ...acc, [e.key]: i } }, {}) as { [key: string]: number }; // Get the header column order. const { handleChangePage, handleChangePageSize, pagedData, isError, isLoading, tryReload, labelDisplay, remove } = useUserTableController(); // Use the controller hook. const rowValues = getRowValues(pagedData?.data, orderMap); // Get the row values. return <DataLoadingContainer isError={isError} isLoading={isLoading} tryReload={tryReload}> {/* Wrap the table into the loading container because data will be fetched from the backend and is not immediately available.*/} <UserAddDialog /> {/* Add the button to open the user add modal. */} {!isUndefined(pagedData) && !isUndefined(pagedData?.totalCount) && !isUndefined(pagedData?.page) && !isUndefined(pagedData?.pageSize) && <TablePagination // Use the table pagination to add the navigation between the table pages. component="div" count={pagedData.totalCount} // Set the entry count returned from the backend. page={pagedData.totalCount !== 0 ? pagedData.page - 1 : 0} // Set the current page you are on. onPageChange={handleChangePage} // Set the callback to change the current page. rowsPerPage={pagedData.pageSize} // Set the current page size. onRowsPerPageChange={handleChangePageSize} // Set the callback to change the current page size. labelRowsPerPage={formatMessage({ id: "labels.itemsPerPage" })} labelDisplayedRows={labelDisplay} showFirstButton showLastButton />} ... </DataLoadingContainer > }
Exista diferite biblioteci care implementeaza aceasta logica, React vine la pachet cu Context API insa