@baaz/adapter
Version:
The core runtime of PWA
119 lines (105 loc) • 3.63 kB
JavaScript
import { useCallback, useMemo, useState } from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { getSearchParam } from './useSearchParam';
/**
* Sets a query parameter in history. Attempt to use React Router if provided
* otherwise fallback to builtins.
*
* @private
*/
const setQueryParam = ({ history, location, parameter, value }) => {
const { search } = location;
const queryParams = new URLSearchParams(search);
queryParams.set(parameter, value);
if (history.push) {
history.push({ search: queryParams.toString() });
} else {
// Use the native pushState. See https://developer.mozilla.org/en-US/docs/Web/API/History_API#The_pushState()_method
history.pushState('', '', `?${queryParams.toString()}`);
}
};
const defaultInitialPage = 1;
/**
* A [React Hook]{@link https://reactjs.org/docs/hooks-intro.html} that provides
* pagination logic.
*
* Use this hook to implement components that need to navigate through paged
* data.
*
* @kind function
*
* @param {Object} config An object containing configuration values
*
* @param {String} config.namespace='' The namespace to append to config.parameter in the query. For example: ?namespace_parameter=value
* @param {String} config.parameter='page' The name of the query parameter to use for page
* @param {Number} config.initialPage The initial current page value
* @param {Number} config.intialTotalPages=1 The total pages expected to be usable by this hook
*
* @return {Object[]} An array with two entries containing the following content: [ {@link PaginationState}, {@link API} ]
*/
export const usePagination = (props = {}) => {
const { namespace = '', parameter = 'page', initialTotalPages = 1 } = props;
const searchParam = namespace ? `${namespace}_${parameter}` : parameter;
const history = useHistory();
const location = useLocation();
// Fetch the initial page value from location to avoid initializing twice.
const initialPage =
props.initialPage ||
parseInt(getSearchParam(searchParam, location) || defaultInitialPage);
const [currentPage, setCurrentPage] = useState(initialPage);
const [totalPages, setTotalPages] = useState(initialTotalPages);
const setPage = useCallback(
page => {
// Update the query parameter.
setQueryParam({
location,
history,
parameter: searchParam,
value: page
});
// Update the state object.
setCurrentPage(page);
},
[history, location, searchParam]
);
/**
* The current pagination state
*
* @typedef PaginationState
*
* @kind Object
*
* @property {Number} currentPage The current page number
* @property {Number} totalPages The total number of pages
*/
const paginationState = { currentPage, totalPages };
/**
* The API object used for modifying the PaginationState.
*
* @typedef API
*
* @kind Object
*/
/**
* Set the current page
*
* @function API.setCurrentPage
*
* @param {Number} page The number to assign to the current page
*/
/**
* Set the total number of pages
*
* @function API.setTotalPages
*
* @param {Number} total The number to set the amount of pages available
*/
const api = useMemo(
() => ({
setCurrentPage: setPage,
setTotalPages
}),
[setPage, setTotalPages]
);
return [paginationState, api];
};