Let’s Build an App with Custom React Hooks
Create custom hooks to search & filter data, fetch data from API and process user input
In this post we will build an app using custom react hooks. Learning along the way, how we can use hooks is different parts of our app. We will process user input, fetch data from API, and search & filter data. All using hooks.
Before we dive in. Let’s have a look at what we will build.
Prerequisites
- Create React App tool installed
- Basic knowledge of React Hooks
Let’s get started
Create a react app with typescript using create-react-app
tool.
create-react-app --template=typescript languages-search-app
Replace the code in App.css
with below code.
.main {
margin-top: 1rem;
}.uppercase {
text-transform: uppercase;
}.text-center {
text-align: center;
}.search-input {
display: block;
margin: auto;
width: 300px;
padding: 0.5rem;
font-size: 0.75rem;
}
App.tsx
code.
import React from "react";
import "./App.css";const App: React.FC = () => {
return (
<div className="main">
<h2 className="text-center uppercase">Programming Languages</h2>
</div>
);
};export default App;
Hook to fetch data from API
Let’s start with hook to fetch data.
import Language from "../types/Language";
import { useState, useEffect } from "react";const useLanguages = () => {
const [languages, setLanguages] = useState<Language[]>([]);
const endpoint = "https://api.npoint.io/33d57619ef599fc57a53";
useEffect(() => {
fetch(endpoint)
.then((response) => response.json())
.then((data) => setLanguages(data))
.catch((error) => console.log({ error }));
}, []); return languages;
};export default useLanguages;
This hook explains itself. First we create state for storing an array of Language
. Where Language
is
export default interface Language {
id: string;
name: string;
}
Then inside the useEffect
hook. We use the fetch
function to fetch the data from API, convert it to JSON and store it in state. Notice, we are passing an empty array as a second argument to useEffect
hook. Which is equal to componentDidMount
in class component.
Save the hook under src/hooks/languagesHook.ts
and Language
type under src/types/Language.ts
.
Let’s use this hook and render the data.
import React from "react";
import "./App.css";import useLanguages from "./hooks/languagesHook";const App: React.FC = () => { const languages = useLanguages(); return (
<div className="main">
<h2 className="text-center uppercase">Programming Languages</h2> {languages.slice(0, 10).map((l) => (
<p className="text-center" key={l.id}>{l.name}</p>
))} </div>
);
};export default App;
The languages list is huge. So I’m limiting it to 10 for now.
Hook to process user input
In class component, to keep track of user input. We have to handle onChange
for every input field and update the value in state
. Using hooks, we can do it better. We can abstract away the code for storing the input value and implementing onChange
. Here is how.
import { useState, useCallback } from "react";const useUserInput = (defaultValue: string = "") => { const [value, setValue] = useState(defaultValue);
const onChange = useCallback((e) => setValue(e.target.value), []);
return {value, onChange,};
};export default useUserInput;
We are using the useCallback
hook so the function doesn’t get recreated for on every render. Let’s see how to use this hook.
import React from "react";
import "./App.css";import useLanguages from "./hooks/languagesHook";
import useUserInput from "./hooks/userInputHook";const App: React.FC = () => { const searchText = useUserInput(""); const languages = useLanguages(); return (
<div className="main">
<h2 className="text-center uppercase">Programming Languages</h2> <input placeholder="Search languages here..."
type="text"
className="search-input text-center"
{...searchText} /> {languages.slice(0, 10).map((l) => (
<p className="text-center" key={l.id}>{l.name}</p>
))} </div>
);
};export default App;
useUserInput
hook returns value
and onChange
in object. We can use the object spread syntax to pass that props to input
.
Now, whenever the user input changes, we can access the value using searchText.value
. This is all good but how we will search through the list of programming languages and filter the data. This brings us to our next hook.
Hook to search and filter list of data
Here it is.
import { useMemo } from "react";const useSearchable = <T>(data: T[], searchText: string, searchProps: (item: T) => string[]) => { return useMemo(() => { const regex = new RegExp(searchText, "i"); return data.filter((item) =>
searchProps(item).some((sp) => regex.test(sp))
); }, [data, searchText, searchProps]);
};export default useSearchable;
Let’s see is what is going on here. First, we have generic param T
. Which allows us to pass any type of array. Then we have 3 function arguments. 1st the array of T
for data, 2nd searchText
to test against array item for match and, searchProps
as function. Because we don’t know, what properties of an object we have to test to determine if the object matches the search. So we take it as an argument. Given the object, return the array of string to test again searchText
to determine it matches or not.
Moving on. We are using the useMemo
hook to memorise the search result and not compute it on every re-render. In useMemo
, we create a RegExp
for searchText
. And filter data using searchProps
function.
Let’s put it to use.
import React from "react";
import "./App.css";import useLanguages from "./hooks/languagesHook";
import useUserInput from "./hooks/userInputHook";
import useSearchable from "./hooks/searchableHook";const App: React.FC = () => { const searchText = useUserInput(""); const languages = useLanguages(); const searchableLanguages = useSearchable(
languages,
searchText.value,
(l) => [l.name]
); return (
<div className="main">
<h2 className="text-center uppercase">Programming Languages</h2> <input placeholder="Search languages here..."
type="text"
className="search-input text-center"
{...searchText} /> {searchableLanguages.slice(0, 10).map((l) => (
<p className="text-center" key={l.id}>{l.name}</p>
))} </div>
);
};export default App;
That’s it. You can checkout the live demo of the app here.
Checkout the git repo.
I hope you learnt something new from this.
Thanks for reading.