Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

emmatraversy's avatar

Inertia Data Table Search Keeps Refreshing The Page

Hi everyone, I have implemented a simple search functionality for users table but for some reason Page is getting refreshed even without typing in searchbox

it gets constantly refreshed:

My Users/Index.jsx file:

import { Head } from '@inertiajs/react';
import Layout from '@/Layouts/Layout';
import Pagination from '@/Components/DataDisplay/Pagination';
import { useState, useEffect } from 'react';
import { router } from '@inertiajs/react'

export default function Index({ users }) {
  const [searchTerm, setSearchTerm] = useState('');

  useEffect(() => {
    router.get(
      '/users',
      { search: searchTerm },
      { preserveState: true, replace: true }
    );
  }, [searchTerm]);

  const handleInputChange = (e) => {
    e.preventDefault();
    setSearchTerm(e.target.value);
  };

  const { data, links } = users;
  return (
    <>
      <Head title="Users" />
      <div>
        <input
          type="text"
          value={searchTerm}
          onChange={handleInputChange}
          placeholder="Search..."
        />
      </div>
      <table className="min-w-full divide-y divide-gray-300">
        <thead>
								.... Table Markup ...
              </td>
            </tr>
          ))}
        </tbody>
      </table>
      <Pagination links={links} />
    </>
  );
}

Index.layout = (page) => <Layout children={page} />;

My simple controller:

<?php

namespace App\Http\Controllers\TestingHub;

use Inertia\Inertia;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Request;


use App\Models\User;

class UserController extends Controller
{
    public function index()
    {
        $users = User::query()
            ->when(Request::input('search'), function ($query, $search) {
                $query->where('name', 'like', "%{$search}%");
            })
            ->paginate(10);
        return Inertia::render('Users/Index', [
            'users' => $users,
        ]);
    }
}

I'm 90% sure the problem is the way I write the useEffect and react stuff but I can't figure it out.

I really appreciate the help. Thanks in advance.

0 likes
7 replies
gych's avatar

Try to implement search with debouncing

You can use a package like lodash for this but here is a simple example:

const searchUsers = () => {
	router.get('/users', { 
		search: searchTerm 
	},{ 
		preserveState: true, 
		replace: true 
	});
}

let searchDebounceTimer;
const handleInputChange = (e) => {
	clearTimeout(searchDebounceTimer);
	setSearchTerm(e.target.value);
	searchDebounceTimer = setTimeout(searchUsers, 500); // change the timeout duration to your preference
};
emmatraversy's avatar

@gych Thank you for your reply

maybe its good to get rid of unnecessary useEffect. but in your case: search term is one character behind, maybe its because you're debouncing the search action after updating the search term state. http://localhost:8000/users?search=tes input:test

gych's avatar

@emmatraversy No problem :) The position of the timeout should have no effect on the value of the input.

Can you test if it uses the correct input when you increase the timeout to 1 second

searchDebounceTimer = setTimeout(searchUsers, 1000);
gych's avatar
gych
Best Answer
Level 29

@emmatraversy Yes you're right I've just quickly tested it and indeed it was always 1 character behind. It has been some time since I used React, mostly working with Vue now but I fixed it and added the full working code below:

import { Head } from '@inertiajs/react';
import Layout from '@/Layouts/Layout';
import Pagination from '@/Components/DataDisplay/Pagination';
import React, { useState, useEffect, useRef } from 'react';
import { router } from '@inertiajs/react'

export default function Index({ users }) {
  const [searchTerm, setSearchTerm] = useState(null);
  const searchDebounceTimer = useRef(null);

  const searchUsers = () => {
    if(searchTerm !== null) {
      	router.get('/users', { 
			search: searchTerm 
		},{ 
			preserveState: true, 
			replace: true 
		});
    }
  };

  useEffect(() => {
    clearTimeout(searchDebounceTimer.current);
    searchDebounceTimer.current = setTimeout(searchUsers, 1000);
  }, [searchTerm]);

  const handleInputChange = (e) => {
	e.preventDefault();
    setSearchTerm(e.target.value);
  };

 const { data, links } = users;
  return (
    <>
      <Head title="Users" />
      <div>
        <input
          type="text"
          value={searchTerm ?? ''}
          onChange={handleInputChange}
          placeholder="Search..."
        />
      </div>
      <table className="min-w-full divide-y divide-gray-300">
        <thead>
								.... Table Markup ...
              </td>
            </tr>
          ))}
        </tbody>
      </table>
      <Pagination links={links} />
    </>
  );
}

Index.layout = (page) => <Layout children={page} />;

EDIT: I've updated the code so it can also handle empty searchTerm to reset the search results

1 like
emmatraversy's avatar

@gych I can't thank u enough for your time and help. Thank you very much. Wish you the best

1 like

Please or to participate in this conversation.