'Table row selection with pagination using Laravel + Inertia + React

I'm trying to display a table that has selectable rows that I will later send with a form to my controller.

For now I got it working but my problem is that in the same table I got a search box and a pagination. They both work together ok but when I have selected rows and then try to search or paginate I lose the state of my selected rows.

This is my component:

import React, {useState} from 'react';
import Input from "@/Components/Form/Input";
import { Inertia } from "@inertiajs/inertia";
import {usePage, useForm, useRemember} from "@inertiajs/inertia-react";
import Main from "../../Layouts/Main"
import Table from "@/Components/Table/Table";
import Search from "@/Components/Search";
import ErrorForm from "@/Components/Form/ErrorForm";
import Paginator from "@/Components/Table/Paginator";
import {usePrevious} from "react-use";

const Create = () => {
    const cuentas = usePage().props.cuentas.data
    const links = usePage().props.cuentas.links
    const { data, processing, errors } = useForm({
        codigoPartida : '',
        nombrePartida : '',
        tipoPartida : 'gasto',
        cuentas : []
    })

    function submit(e) {
        e.preventDefault()
        data.codigoPartida = document.getElementById('codigoPartida').value
        data.nombrePartida = document.getElementById('nombrePartida').value
        document.querySelectorAll('.row-select').forEach(function(item){
            if(!data.cuentas.includes(item)){
                if(item.checked){
                    data.cuentas.push(item.value)
                }
            }
        });
        console.log(data)
        //Inertia.post('/nueva-partida-presupuestaria',data)
    }

    function handleChange(e){
        if(e.target.checked){
            document.querySelectorAll('.row-select').forEach(function(item){
                item.checked = true
            })
        }else{
            document.querySelectorAll('.row-select').forEach(function(item){
                item.checked = false
            })
        }
    }

    return (
        <>
            <div className="flex justify-between mb-2">
                <h2>Alta Partida Presupuestaria</h2>
            </div>

            <form className="create-form" id="create-form" onSubmit={submit}>
                <Input
                    name="codigoPartida"
                    type="text"
                    id="codigoPartida"
                    placeholder="Código"
                />
                {errors.codigoPartida &&
                    <ErrorForm
                        content={errors.codigoPartida}
                    />
                }
                <Input
                    name="nombrePartida"
                    type="text"
                    id="nombrePartida"
                    placeholder="Descripción"
                />
                {errors.codigoPartida &&
                    <ErrorForm
                        content={errors.codigoPartida}
                    />
                }
                <button className="btn-consejo" type="submit" disabled={processing}>Crear</button>
            </form>
            <h2>Agregar Cuenta Contable</h2>
            <div className="flex justify-between">
                <Search />
            </div>
            <table className="min-w-full">
                <thead>
                <tr>
                    <th>
                        <input
                            id="select-all"
                            name="select-all"
                            type="checkbox"
                            onChange={handleChange}
                        />
                    </th>
                    <th className="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-gray-500 uppercase border-b border-gray-200 bg-gray-504">Codigo</th>
                    <th className="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-gray-500 uppercase border-b border-gray-200 bg-gray-504">Cuenta</th>
                    <th className="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-gray-500 uppercase border-b border-gray-200 bg-gray-504">Clase</th>
                </tr>
                </thead>
                <tbody>
                {cuentas.map(({ id, COD_CUENTA, DESC_CUENTA, ID_CLASE_CUENTA }) => {
                    return (
                        <tr key={id}>
                            <td><input type="checkbox" value={id} name="row-select" className="row-select" /></td>
                            <td>{COD_CUENTA}</td>
                            <td>{DESC_CUENTA}</td>
                            <td>{ID_CLASE_CUENTA}</td>
                        </tr>
                    )
                })}
                {cuentas.length === 0 && (
                    <tr>
                        <td colSpan="4">
                            No se encontraron cuentas disponibles.
                        </td>
                    </tr>
                )}
                </tbody>
            </table>
            <Paginator links={links} />
        </>
    );
}

Create.layout = page => <Main title="Alta Partida Presupuestaria" children={page} />

export default Create

And also my Paginator and Search Components:

import React from 'react';
import { InertiaLink } from '@inertiajs/inertia-react';

const PageLink = ({ active, label, url }) => {
    return (
        <InertiaLink href={url} preserveState>
            <span dangerouslySetInnerHTML={{ __html: label }}></span>
        </InertiaLink>
    );
};

// Previous, if on first page
// Next, if on last page
// and dots, if exists (...)
const PageInactive = ({ label }) => {
    return (
        <div dangerouslySetInnerHTML={{ __html: label }} />
    );
};

export default ({ links = [] }) => {
    // dont render, if there's only 1 page (previous, 1, next)
    if (links.length === 3) return null;

    return (
        <div className="flex flex-wrap mt-6 -mb-1">
            {links.map(({ active, label, url }) => {
                return url === null ? (
                    <PageInactive key={label} label={label} />
                ) : (
                    <PageLink key={label} label={label} active={active} url={url} />
                );
            })}
        </div>
    );
};

import React, { useState, useEffect, useRef } from 'react';
import { Inertia } from '@inertiajs/inertia';
import {usePage, useRemember} from '@inertiajs/inertia-react';
import { usePrevious } from 'react-use';
import pickBy from 'lodash/pickBy';

export default () => {
    const { filters } = usePage().props;

    const [values, setValues] = useState({
        search: filters.search || '',
    });

    const prevValues = usePrevious(values);

    useEffect(() => {
        // https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
        if (prevValues) {
            const query = pickBy(values)
            Inertia.get(route(route().current()), query, {
                replace: true,
                preserveState: true
            });
        }
    }, [values]);

    function handleChange(e) {
        const key = e.target.name;
        const value = e.target.value;

        setValues(values => ({
            ...values,
            [key]: value
        }));
    }

    return (
        <div className="flex items-center w-full max-w-md mr-4">
            <div className="relative flex w-full bg-white rounded shadow">
                <input
                    className="relative w-full px-6 py-3 rounded-r focus:outline-none focus:ring-2 focus:ring-indigo-400"
                    autoComplete="off"
                    type="text"
                    name="search"
                    name="search"
                    value={values.search}
                    onChange={handleChange}
                    placeholder="Search…"
                />
            </div>
        </div>
    )
}

How would I go to get the expected behaviour. I'm kind of new to react.

I would really appreciate the help!

Image of the UI



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source