Custom Hooks in React(Data Fetching)
Hooks that we create ourself, so other people can use them are called custom hooks.
A custom hook is effectively a function but with the following properties —
- Uses another hook internally (useState, useEffect, another custom hook)
- Starts with use
A few good examples of this can be —
- Data fetching hooks
- Browser functionality related hooks — useOnlineStatus, useWindowSize, useMousePosition
- Preformance/Timer based — useInterval, useDebounce
Data Fetching hooks
Data fetching hooks can be used to encapsulate all the logic to fetch the data from your backend.
For example, look at the following code-
import { useEffect, useState } from 'react'
import axios from 'axios'
function App() {
const [todos, setTodos] = useState([])
useEffect(() => {
axios.get("Backend URL that renders a list of todos")
.then(res => {
setTodos(res.data.todos);
})
}, [])
return (
<>
{todos.map(todo => <Track todo={todo} />)}
</>
)
}
function Track({ todo }) {
return <div>
{todo.title}
<br />
{todo.description}
</div>
}
export default App
From the above code, don’t you think our app component is doing a lot of task, and let us assume that we have many components in our application where we have to do the same stuff where we have to fetch data form the backend server. So, to comply with the property of DRY Principle we can use the custom hook where we are just fetching data from backend and whenever the same thing is required in any component, we can use it easily.
Step 1 — Converting the data fetching bit to a custom hook
import { useEffect, useState } from 'react'
import axios from 'axios'
function useTodos() {
const [todos, setTodos] = useState([])
useEffect(() => {
axios.get("Backend url to get todos")
.then(res => {
setTodos(res.data.todos);
})
}, [])
return todos;
}
function App() {
const todos = useTodos();
return (
<>
{todos.map(todo => <Track todo={todo} />)}
</>
)
}
function Track({ todo }) {
return <div>
{todo.title}
<br />
{todo.description}
</div>
}
export default App
Step 2 — Cleaning the hook to include a loading parameter
What if you want to show a loader when the data is not yet fetched from the backend?
import { useEffect, useState } from 'react'
import axios from 'axios'
function useTodos() {
const [loading, setLoading] = useState(true);
const [todos, setTodos] = useState([])
useEffect(() => {
axios.get("url to enter")
.then(res => {
setTodos(res.data.todos);
setLoading(false);
})
}, [])
return {
todos: todos,
loading: loading
};
}
function App() {
const { todos, loading } = useTodos();
if (loading) {
return <div>
Loading...
</div>
}
return (
<>
{todos.map(todo => <Track todo={todo} />)}
</>
)
}
function Track({ todo }) {
return <div>
{todo.title}
<br />
{todo.description}
</div>
}
export default App
Step 3 — Auto refreshing hook
What if you want to keep polling the backend every n seconds? n needs to be passed in as an input to the hook
import { useEffect, useState } from 'react'
import axios from 'axios'
function useTodos(n) {
const [todos, setTodos] = useState([])
const [loading, setLoading] = useState(true);
useEffect(() => {
const value = setInterval(() => {
axios.get("url to enter")
.then(res => {
setTodos(res.data.todos);
setLoading(false);
})
}, n * 1000)
axios.get("https://sum-server.100xdevs.com/todos")
.then(res => {
setTodos(res.data.todos);
setLoading(false);
})
return () => {
clearInterval(value)
}
}, [n])
return {todos, loading};
}
function App() {
const {todos, loading} = useTodos(10);
if (loading) {
return <div> loading... </div>
}
return (
<>
{todos.map(todo => <Track todo={todo} />)}
</>
)
}
function Track({ todo }) {
return <div>
{todo.title}
<br />
{todo.description}
</div>
}
export default App