DEV Community

Cover image for A Junior approach to react-pagination with Laravel 9.
Madalena Rio
Madalena Rio

Posted on • Updated on

A Junior approach to react-pagination with Laravel 9.

Cover photo by Tim Wildsmith on Unsplash

snapshot of footer section of website showing round buttons navigation
As I write this, I'm quite new to programming. I only have 7 months of an intensive bootcamp for Full stack web developer on my back.
I'm still on training wheels, and my biggest resources are google, Dev.to and Stack overflow.
I hear from senior developer friends that those never really come off.

"Most of us can't code from memory only, there is always web search involved somewhere along the process." - they say.
"Focus on learning the basic logic structures of the languages and frameworks, the rest is just syntax." - they advised me.

Well then! That sounds simple enough...šŸ˜’

In theory it is simple. Well all programming languages have the same basic concepts for data structure, control structure and sequencing, right?
Here is a very nice article on this subject.

But when you jump from language to language, framework to framework, it can become quite confusing. Everything is still so raw in your "junior" memory that you just mix everything.

Then came a Laravel/React social platform project

So when I had to work on the pagination feature for a social platform project made with Laravel and React, I felt overwhelmed. I had had only one week of React/Laravel experience.

Well the whole social platform project was overwhelming but I will only focus on this simple feature for this article... right... šŸ™ƒ let's keep going shall we?

I remembered the advice I was given, and began this saga with the usual google search.

Let me see how others did it? šŸ¤”.

snapshot of google search input field with sentence "Laravel React pagination

After long stretches of production time spent on endless react components and youtube video tutorials and /r/AskReddit or /r/HoldMyBeer...

Wait! What?šŸ˜
... how did I get there...?
errmmm... šŸ˜

I was more confused than before.
If I got it right, I would have to create a component file inside my MVC folder for components, that would be full with logic that seemed complicated math stuff.

My skills at math don't go beyond the simple calculator any windows or android or mac OP system has, it's embarrassing.šŸ˜¬

And then would have to import this component to any page I wanted to use it in.

Right! okay, what was that advice again...? šŸ˜•

I had done a very easy pagination before. In a simple Laravel/Breeze/Tailwind project. Where all I had to do, was to call the in built method "paginate" on the query builder and then add a line of code on the blade page and it worked perfectly!

Query builder

class UserController extends Controller
{
    /**
     * Show all application users.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('user.index', [
            'users' => DB::table('users')->paginate(15)
        ]);
    }
}
Enter fullscreen mode Exit fullscreen mode

blade page

<div class="container">
    @foreach ($users as $user)
        {{ $user->name }}
    @endforeach
</div>

{{ $users->links() }}
Enter fullscreen mode Exit fullscreen mode

You can learn more on that here:
Laravel pagination doc

Alright, but for this project all the front end was built with React, so I could not use this. On the other hand for sure I was not the only developer in the world that had to make a pagination with React. For sure there was a similar package or plug-in, something of this sort that would make all developers life a bit easier when it came to pagination.

This is something Iā£ļø in the developers & IT world community! It's the fact that everyone shares knowledge!
There is this in-built explorer spirit that loves to go out there into the unknown, like the sea explorers of the 14th century, and battle those sea monsters, overcome challenges, to find new perspectives and more efficient solutions, not to reinvent the wheel but to build on it and grow. Because who knows where it will takes us? What new and wonderful worlds we can build? And one thing is for sure if 2 heads think better than one, 26.9 million of us šŸ¤Æ will think much better!

Therefor I was certain that I wouldn't need to build a pagination from scratch. Maybe there was a similar package approach or built in component that would do the job?
So, after a bit more searching I finally found what I wanted.

Npm React-paginate doc

I just had to download the package and add a bit more lines of code than with the Laravel solution, but most of the hard work that was difficult for my newbie skills was handled by the package.

The Laravel-react project was already installed and the authentication, database tables, migrations and page routing was all being handled by Laravel & Breeze in the back end (my group colleagues where developing that part). So I could pull up my sleeves and jump directly to work on building the page view for the section o the platform I was responsible for and then adding the pagination feature.

Let's do this!

I began by installing the package:

npm install react-paginate --save
Enter fullscreen mode Exit fullscreen mode

Image description

After building a basic html view, adding all components like the "posts" card and placing all the elements from the array I wanted to "map" and properly display the "Post", I felt I had enough of a foundation to bring in the pagination.

import { Head } from "@inertiajs/inertia-react";
import CentralLogo from "@/Components/CentralLogo";
import DarkBlueBlockHeader from "@/Components/DarkBlueBlockHeader";
import PostCard from "@/Components/PostCard";

export default function Profile(props) {
    return (
        <>
            <Head title="Profile" />

            <div className="relative w-full h-fit flex flex-col overflow-auto items-center">
                <DarkBlueBlockHeader className="">
                    <section className="flex-row">
                        <h1 className="font-bold text-gray-100 text-5xl m-4 p-2">
                            Hello {props.auth.user.name}
                        </h1>
                    </section>
                    <CentralLogo
                        width="100px"
                        src="images/icon/waldo.png"
                        alt="user avater image"
                    />

                    <h1 className="text-center text-gray-400 text-2xl m-4 p-2">
                        Lost in posts?
                        <br />
                        This page contains them all.
                    </h1>
                </DarkBlueBlockHeader>
            </div>

            <div className="flex flex-col max-w-full sm:pt-10">
                <section className="absolute right-0 mr-32">
                    <SearchBar />
                </section>

                <div className="flex flex-col justify-center items-center sm:items-center sm:pt-10">
                    <section className="mb-6">
                        <h1 className="font-bold text-2xl">My Posts</h1>

                        {posts.map(
                            ({ id, title, content, tag, page, user }) => {
                                return (
                                    <PostCard key={id}>
                                        <h1 className="mt-1 font-bold text-gray-700">
                                            {title}
                                        </h1>

                                        <p className="ml-2 mb-4 text-gray-700">
                                            <b>by {user.name}</b>
                                        </p>

                                        <div
                                            id="postContent"
                                            type="textfield"
                                            className="flex flex-col ml-2 mb-5 w-[500px] text-ellipsis "
                                        >
                                            {content}
                                        </div>
                                        <p className="ml-2 mb-1 text-lg font-bold text-gray-600">
                                            <b className="text-lightBlue">
                                                From: {page}
                                            </b>
                                        </p>

                                        <p className="ml-2 mb-1 font-small text-gray-600">
                                            <b className="text-lightBlue">
                                                tags: {tag}
                                            </b>
                                        </p>
                                    </PostCard>
                                );
                            }
                        )}
                    </section>
                </div>
            </div>

            <div className="h-64">
                <Footer></Footer>
            </div>
        </>
    );
}

Enter fullscreen mode Exit fullscreen mode

But off course I would then have to adapt the npm tutorial logic to my logic.
Now comes the tricky part.
So I broke it down in simple steps:

First:

Make use of the React hook useState to set the initial state of the array of items. The target here was the array of posts being created by the platform users so I called this variable "posts".
It also seemed logic then that I would pass this array being built and used in other "corners of this platform", along in my function as a props argument props.posts.

Second:

Do the same for the initial state of the page displaying the posts. The variable I called "page" and set it to 0.

Third:

Create the logic flow:

  • Posts per page displayed will be 3;
  • Number of posts visited will be equal to the page visited multiplied by the number of posts displayed per page.
  • Then in order to keep track of the total number of pages visited and keep moving forward in the array, I have to divide the total "length of the posts array" by the number of "post per page" and round that result to an integer using the method Math.ceil().
  • Finally I need to set an event listener function or in this case a "onPageChange" method in order to make all this logic happen when the user clicks the next or previous page button.

And here is the overview of all of this pagination logic:

    //// pagination ////

    const [posts] = useState(props.posts);
    const [page, setPage] = useState(0);
    const postsPerPage = 3;
    const numberOfPostsVisited = page * postsPerPage;
    const totalPages = Math.ceil(posts.length / postsPerPage);
    const changePage = ({ selected }) => {
        setPage(selected);
    };

    //// pagination ////
Enter fullscreen mode Exit fullscreen mode

Right! The flow was set but there was still some pieces of this pagination recipe puzzle missing.
The array of data still needed to be correctly distributed (sliced) trough the navigation pages, and I also needed to assign all the props properties from this package component to the correct variables that will make this logic recipe "bake the pagination cake".

Fourth

Now to implement the rest of the ingredients and cook this batter:


<section className="mb-6">
    <h1 className="font-bold text-2xl">My Posts</h1>

    {posts
        .slice(numberOfPostsVisited, numberOfPostsVisited + postsPerPage)
        .map(({ id, title, content, tag, page, user }) => {
            return (
                <PostCard key={id}>
                    <h1 className="mt-1 font-bold text-gray-700">{title}</h1>

                    <p className="ml-2 mb-4 text-gray-700">
                        <b>by {user.name}</b>
                    </p>

                    <div
                        id="postContent"
                        type="textfield"
                        className="flex flex-col ml-2 mb-5 w-[500px] text-ellipsis "
                    >
                        {content}
                    </div>
                    <p className="ml-2 mb-1 text-lg font-bold text-gray-600">
                        <b className="text-lightBlue">From: {page}</b>
                    </p>

                    <p className="ml-2 mb-1 font-small text-gray-600">
                        <b className="text-lightBlue">tags: {tag}</b>
                    </p>
                </PostCard>
            );
        })}

    <section className="mx-4 my-4">
        <ReactPaginate
            pageRangeDisplayed={5} //The range of buttons pages displayed.
            previousLabel={"Previous"} //lable for previous page button
            nextLabel={"Next"} // lable for Next page button
            pageCount={totalPages} // place here the variable for total number of pages
            onPageChange={changePage} // place here the trigger event function
            /// navigation CSS styling ///
            containerClassName={"navigationButtons"}
            previousLinkClassName={"previousButton"}
            nextLinkClassName={"nextButton"}
            disabledClassName={"navigationDisabled"}
            activeClassName={"navigationActive"}
            /// end navigation styling ///
        />
    </section>
</section>;

Enter fullscreen mode Exit fullscreen mode

My last thoughts

My simple naĆÆve approach was far from good but it got the work done. I was not able to make it work like a separate component due to my lack of experience with React and had therefor to implement this "recipe" in every page we had a need for pagination, but here comes the beauty of development, even with only one week of experience with React and Laravel I was able to make a functional pagination feature for my Social Platform project.
I was thrilled with my newbie result. :)

A final overview!

import React, { useState, useEffect } from "react";
import { Head } from "@inertiajs/inertia-react";
import CentralLogo from "@/Components/CentralLogo";
import DarkBlueBlockHeader from "@/Components/DarkBlueBlockHeader";
import PostCard from "@/Components/PostCard";

export default function Profile(props) {

    /// The useState attributed to the posts array is being used to set intial sate for the pagination to use the array of posts brought in by the global variable props ////
    const [posts] = useState(props.posts);


    //// pagination ////
    const [page, setPage] = useState(0);
    const postsPerPage = 3;
    const numberOfPostsVisited = page * postsPerPage;
    const totalPages = Math.ceil(posts.length / postsPerPage);
    const changePage = ({ selected }) => {
        setPage(selected);
    };
    //// pagination ////

    return (
        <>
            <Head title="Profile" />

            <div className="relative w-full h-fit flex flex-col overflow-auto items-center">
                <DarkBlueBlockHeader className="">
                    <section className="flex-row">
                        <h1 className="font-bold text-gray-100 text-5xl m-4 p-2">
                            Hello {props.auth.user.name}
                        </h1>
                    </section>
                    <CentralLogo
                        width="100px"
                        src="images/icon/waldo.png"
                        alt="user avater image"
                    />

                    <h1 className="text-center text-gray-400 text-2xl m-4 p-2">
                        Lost in posts?
                        <br/>This page contains them all.
                    </h1>
                </DarkBlueBlockHeader>
            </div>

            <div className="flex flex-col max-w-full sm:pt-10">
                <section className="absolute right-0 mr-32">
                    <SearchBar />
                </section>

                <div className="flex flex-col justify-center items-center sm:items-center sm:pt-10">
                <section className="mb-6">
    <h1 className="font-bold text-2xl">My Posts</h1>

    {posts
        .slice(numberOfPostsVisited, numberOfPostsVisited + postsPerPage)
        .map(({ id, title, content, tag, page, user }) => {
            return (
                <PostCard key={id}>
                    <h1 className="mt-1 font-bold text-gray-700">{title}</h1>

                    <p className="ml-2 mb-4 text-gray-700">
                        <b>by {user.name}</b>
                    </p>

                    <div
                        id="postContent"
                        type="textfield"
                        className="flex flex-col ml-2 mb-5 w-[500px] text-ellipsis "
                    >
                        {content}
                    </div>
                    <p className="ml-2 mb-1 text-lg font-bold text-gray-600">
                        <b className="text-lightBlue">From: {page}</b>
                    </p>

                    <p className="ml-2 mb-1 font-small text-gray-600">
                        <b className="text-lightBlue">tags: {tag}</b>
                    </p>
                </PostCard>
            );
        })}

    <section className="mx-4 my-4">
        <ReactPaginate
            pageRangeDisplayed={5} //The range of buttons pages displayed.
            previousLabel={"Previous"} //lable for previous page button
            nextLabel={"Next"} // lable for Next page button
            pageCount={totalPages} // place here the variable for total number of pages
            onPageChange={changePage} // trigger event function
            /// navigation styling ///
            containerClassName={"navigationButtons"}
            previousLinkClassName={"previousButton"}
            nextLinkClassName={"nextButton"}
            disabledClassName={"navigationDisabled"}
            activeClassName={"navigationActive"}
            /// end navigation styling ///
        />
    </section>
</section>;


                </div>
            </div>

            <div className="h-64">
                <Footer></Footer>
            </div>
        </>
    );
}


Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
lbash profile image
bash

Hello, to separate the pagination logic into another component, you could do the following:

import { Link } from '@inertiajs/react'

export default function Paginator ({ pagination }) {
    return (
        <>
            <nav className="relative flex justify-center">
                {pagination.links.map((link) => {
                    return (
                        <Link key={link.label}
                            href={link.url ?? ''}
                            className="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-cyan-950 hover:text-white"
                        >
                            {link.label}
                        </Link>

                    )
                })}
            </nav>
        </>
    )
}

Enter fullscreen mode Exit fullscreen mode

and all that remains is to pass the pagination object to it.

<Paginator pagination={posts} />
Enter fullscreen mode Exit fullscreen mode

You don't need to use posts.slice.map, you just need to use posts.data.map().

Collapse
 
lbash profile image
bash

PD: Great Post !!