I have several components/pages in my personal project that contain complex logic and even nested ternary operators. Since I'm the only person working on this project, I don't have a hard time figuring out where to make changes if I need to.
But I'm trying to improve my code in a way that others could work on it too. I am looking for some input to understand what kind of logic is acceptable.
Below is an example.
This component is a reusable pagination bar. By default, it renders links for each page number, which, when clicked, modify the ?page
query param of the current URL. This behavior can be overridden by passing an optional onPageSelected callback, which renders each page number as a button with an onClick handler instead.
This logic makes the pagination bar reusable within my app but as you can see, the code also becomes quite complex.
- (How) would you change this?
- Do you think I'm making the code too complex by reusing the same component for the link- and button variations?
- Would you use separate components for rendering links & buttons?
- Would you move the ternary operators into variables? Anything else?
import Link from "next/link";
import { useRouter } from "next/router";
import { Pagination } from "react-bootstrap";
interface PaginationBarProps {
pageCount: number,
currentPage: number,
onPageSelected?: (page: number) => void,
}
const PaginationBar = ({ pageCount, currentPage, onPageSelected }: PaginationBarProps) => {
const router = useRouter();
const paginationMinPage = Math.max(1, currentPage - 5);
const paginationMaxPage = Math.min(pageCount, Math.max(currentPage + 4, 10));
function getHrefForPage(page: number) {
const href = new URL("http://localhost:3000" + router.asPath);
href.searchParams.set('page', page.toString());
return href.toString();
}
const pageItems: JSX.Element[] = [];
for (let i = paginationMinPage; i <= paginationMaxPage; i++) {
let paginationItem: JSX.Element;
if (i === currentPage) {
paginationItem =
<>
<Pagination.Item active className='d-none d-md-block' key={i}>{i}</Pagination.Item>
<Pagination.Item active className='d-sm-block d-md-none' key={i + "-mobile"}>Page: {i}</Pagination.Item>
</>
} else {
if (onPageSelected) {
paginationItem =
<Pagination.Item
key={i}
className='d-none d-md-block'
onClick={() => onPageSelected(i)}>
{i}
</Pagination.Item>
} else {
paginationItem =
<Link href={getHrefForPage(i)} passHref legacyBehavior key={i}>
<Pagination.Item className='d-none d-md-block'>
{i}
</Pagination.Item>
</Link>
}
}
pageItems.push(paginationItem);
}
return (
<Pagination className="mt-4">
{currentPage > 1 &&
(
onPageSelected
?
<>
<Pagination.First onClick={() => onPageSelected(1)} key="first" />
<Pagination.Prev onClick={() => onPageSelected(Math.max(currentPage - 1, 1))} key="prev" />
</>
:
<>
<Link href={getHrefForPage(1)} passHref legacyBehavior key="first">
<Pagination.First />
</Link>
<Link href={getHrefForPage(Math.max(currentPage - 1, 1))} passHref legacyBehavior key="prev">
<Pagination.Prev />
</Link>
</>
)
}
{pageItems}
{currentPage < pageCount &&
(
onPageSelected
?
<>
<Pagination.Next onClick={() => onPageSelected(Math.min(currentPage + 1, pageCount))} key="next" />
<Pagination.Last onClick={() => onPageSelected(pageCount)} key="last" />
</>
:
<>
<Link href={getHrefForPage(Math.min(currentPage + 1, pageCount))} passHref legacyBehavior key="next">
<Pagination.Next />
</Link>
<Link href={getHrefForPage(pageCount)} passHref legacyBehavior key="last">
<Pagination.Last />
</Link>
</>
)
}
</Pagination>
);
}
export default PaginationBar;
```