...
/Adding a useDebounce React Hook
Adding a useDebounce React Hook
Learn how to optimize and reduce application rerenders from React onChange events via a custom useDebounce hook.
While everything with our generic search function is working well, we can optimize how the SearchInput
function calls the setSearchQuery
callback.
Monitor renderings
Let’s investigate what our search function currently does by adding a console.log()
call to the onChange
event within SearchInput
.
import * as React from "react";export interface ISearchInputProps {setSearchQuery: (searchQuery: string) => void;}export function SearchInput(props: ISearchInputProps) {const { setSearchQuery } = props;return (<><label htmlFor="search" className="mt-3">Search! Try me!</label><inputid="search"className="form-control full-width"type="search"placeholder="Search..."aria-label="Search"onChange={(event) => {// Monitor when this event firesconsole.log("Firing!");setSearchQuery(event.target.value)}}/></>);}
Likewise, add a console.log()
that says Rendering!
to App.tsx
.
Now, when we run our app and look in the browser console, we should see a Rendering!
log for every Firing!
log.
Firing!Rendering!Firing!Rendering!Firing!Rendering!...
With some modifications, we can simulate the console.log
messages by appending them right into our chat. We can check out the application in action here and see how every keystroke entered in the search field causes a rerender.
import * as React from 'react'; import { useState } from 'react'; import { widgets } from './mock-data/widgets'; import { people } from './mock-data/people'; import { genericSearch } from './utils/genericSearch'; import { SearchInput } from './components/SearchInput'; export default function App() { const [query, setQuery] = useState(""); const [consoleLogs, setConsoleLogs] = useState<Array<string>>([]) console.log("Rendering!") return ( <> <p>Simulated Console Output:</p> <pre style={{height: "100px", overflowY: "scroll"}}>{consoleLogs.join("\n")}</pre> <SearchInput setSearchQuery={setQuery} onConsoleLog={newLogs => setConsoleLogs([...consoleLogs, ...newLogs])}/> <h2>Widgets:</h2> {widgets.filter((widget) => genericSearch(widget, ["title", "description"], query, false)).map(widget => { return ( <h3>{widget.title}</h3> ) })} <h2>People:</h2> {people.filter((person) => genericSearch(person, ["firstName", "lastName", "eyeColor"], query, false)).map(person => { return ( <h3>{person.firstName} {person.lastName}</h3> ) })} </> ) }
This is where we have a performance optimization opportunity. It’s suboptimal to rerender our search, which includes refiltering our entire widgets and people lists, immediately after each ...