简体   繁体   中英

How to use pagination with React router v6

I'm trying to implement a pagination with react router v6. The pagination itself is working, but every time the page is refreshed it goes back to page 1. I was thinking having the pagination number in the url. Here it works for page 1, but it doesn't update the page number on click on next button. How could I fix this?

This is my code:

function PostsList({ page, setPage }) {
    // Get data
    const [allPosts, setAllPosts] = useState([])
    const [pages, setPages] = useState(0)

    useEffect(() => {
        axios
            .get("/posts/posts")
            .then(res => {
                setAllPosts(res.data)
                setPages(Math.round(res.data.length / dataLimit))
            })
            .catch(err => console.log(err))
    }, [])

    // Pagination
    const dataLimit = 10
    const [currentPage, setCurrentPage] = useState(page)

    const goToNextPage = () => {
        setCurrentPage(page => page + 1)
        setPage(page => page + 1)
    }

    const goToPreviousPage = () => {
        setCurrentPage(page => page - 1)
        setPage(page => page - 1)
    }

    const changePage = e => {
        const pageNumber = Number(e.target.textContent)
        setCurrentPage(pageNumber)
    }

    const getPaginatedData = () => {
        const startIndex = currentPage * dataLimit - dataLimit
        const endIndex = startIndex + dataLimit
        return allPosts.slice(startIndex, endIndex)
    }

    const getPaginationGroup = () => {
        let start = Math.floor((currentPage - 1) / pages) * pages
        return new Array(pages).fill().map((_, i) => start + i + 1)
    }

    return (
        <Page>
            <ListPosts>
                {getPaginatedData().map(post => (
                    <Card post={post} key={post._id} />
                ))}
            </ListPosts>

            {getPaginationGroup().length > 0 && (
                <PaginationContainer>
                    <PaginationButton
                        onClick={goToPreviousPage}
                        disabled={currentPage === 1 && "disabled"}
                    >
                        <Icon
                            name="chevron-left"
                            color="currentColor"
                            size={16}
                        />
                    </PaginationButton>

                    {getPaginationGroup().map((item, i) => (
                        <PaginationButton
                            onClick={changePage}
                            className={currentPage === item && "active"}
                            key={i}
                        >
                            {item}
                        </PaginationButton>
                    ))}

                    <PaginationButton
                        onClick={goToNextPage}
                        disabled={currentPage === pages && "disabled"}
                    >
                        <Icon
                            name="chevron-right"
                            color="currentColor"
                            size={16}
                        />
                    </PaginationButton>
                </PaginationContainer>
            )}
        </Page>
    )
}

export default PostsList

And the Routes:

function Switch() {
    const [page, setPage] = useState(1)

    return (
        <Routes>
            <Route
                exact
                path="/posts/:page"
                element={<PostsList page={page} setPage={setPage} />}
            />
        </Routes>
    )
}

export default Switch

Thanks for your help!

You're duplicating your route state by holding it in local component state and only updating the local component state. Instead, make sure to use the route as the source of truth for your routing state and use tools like programmatic routing and link components to update your page state.

function Switch() {
    return (
        <Routes>
            <Route
                exact
                {/* :page here the "state" of the route, we don't need to duplicate it. */} 
                path="/posts/:page"
                element={<PostsList />}
            />
        </Routes>
    )
}

export default Switch

See: https://reactrouter.com/docs/en/v6/hooks/use-navigate for details on programmatic navigation,

function PostsList() {
    // Get data
    const [allPosts, setAllPosts] = useState([])
    const [pages, setPages] = useState(0)

    useEffect(() => {
        axios
            .get("/posts/posts")
            .then(res => {
                setAllPosts(res.data)
                setPages(Math.round(res.data.length / dataLimit))
            })
            .catch(err => console.log(err))
    }, [])

    // Pagination
    const dataLimit = 10
    const { page: currentPage }  = useParams(); // from react-router, this is the `:page` parameter defined on the route. 
    const navigate = useNavigate(); 

    const goToNextPage = () => {
        navigate(`../${currentPage + 1}`) // you may have to tweak this based on your routing. See the link above about useNavigate()
    }

    const goToPreviousPage = () => {
        navigate(`../${currentPage - 1}`)
    }

    const changePage = e => {
        const pageNumber = Number(e.target.textContent)
        navigate(`../${pageNumber}`)
    }

    const getPaginatedData = () => {
        const startIndex = currentPage * dataLimit - dataLimit
        const endIndex = startIndex + dataLimit
        return allPosts.slice(startIndex, endIndex)
    }

    const getPaginationGroup = () => {
        let start = Math.floor((currentPage - 1) / pages) * pages
        return new Array(pages).fill().map((_, i) => start + i + 1)
    }

    return (
        <Page>
            <ListPosts>
                {getPaginatedData().map(post => (
                    <Card post={post} key={post._id} />
                ))}
            </ListPosts>

            {getPaginationGroup().length > 0 && (
                <PaginationContainer>
                    <PaginationButton
                        disabled={currentPage === 1 && "disabled"}
                    >
                    <NavLink to={`../${currentPage - 1}`}> // This might need to change slightly based on your needs, or could use the programatic approach above. Same for the other buttons below.
                        <Icon
                            name="chevron-left"
                            color="currentColor"
                            size={16}
                        />
                    </NavLink>
                    </PaginationButton>

                    {getPaginationGroup().map((item, i) => (
                        <PaginationButton
                            className={currentPage === item && "active"}
                            key={i}
                        >
                            {item}
                        </PaginationButton>
                    ))}

                    <PaginationButton
                        disabled={currentPage === pages && "disabled"}
                    >
                        <Icon
                            name="chevron-right"
                            color="currentColor"
                            size={16}
                        />
                    </PaginationButton>
                </PaginationContainer>
            )}
        </Page>
    )
}

export default PostsList

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM