Building and using custom React hooks in our application
Hooks are the new trend in React Community and are inevitable for any React developer who prefers functional components to the class base components. Hooks changes the way our React code is written, making our code clean and more readable. In this article, we will learn what is custom hooks? how to create React custom hooks? and how to use this hook in our application?
Hooks
They are typical javascript functions used in functional components and allow us to use state and life cycle methods like componentDidUpdate
, componentDidMount
, and more in functional components. It plays a major role in code reusability as it allows us to reuse component and state logic across different components.
Custom hooks:
These are normal javascript functions which contain other hooks inside of it alongside with some common stateful logic that can be reused within multiple components. These functions usually begin with the word use
.
Before the arrival of custom hooks, were able to handle repeated and redundant stateful logic inside multiple components by relying on Render props and Higher Order Components. But with the custom hooks, we can accomplish this in a much simpler and cleaner way.
Rules of Hooks
They are two rules to follow when using hooks. Fortunately, react provide a linter plugin to enforce these rules automatically:
Rule 1:
Do not call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState
and useEffect
calls.
Rule 2:
Only Call Hooks from React Functions
Don’t call Hooks from regular JavaScript functions. Instead, you can:
- Call Hooks from React function components.
- Call Hooks from custom Hooks.
By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.
Now that we are conversant with these rules, let’s build our custom react hook
Building our React Custom hooks
Before we start building our custom hooks, npm should be our best guide because there is high possibility that someone has already published it on npm.
Let’s build our first custom hook. First, we need to create an app using create react command as follows:
npx create-react-app custom-hooks
Next, run the following command:
cd custom-hooks
npm start
useOrderCount hook
Navigate to the src folder and create a hooks folder containing a file useOrderCount.js
to write our custom hook logic as follow:
import { useState } from 'react';
function useOrderCountHook() {
const [orderCount, setOrderCount] = useState(0);
const incrementOrderCount = () => {
setOrderCount( orderCount + 1 )
}
const decrementOrderCount = () => {
setOrderCount( orderCount - 1 )
}
return { orderCount, incrementOrderCount, decrementOrderCount };
}
export default useOrderCountHook;
In the code snippet above, we imported the default react hook useState
to create a state orderCount
with an initial value of 0. Next we created our hook useOrderCountHook
which is just a normal javascript function containing the incrementOrderCount
and decrementOrderCount
function to mutate the orderCount
Now in the above code, we have built our first custom hook orderCountHook
and it is ready for use.
Using the **useOrderCountHook**
in other components:
In the src folder, create a component folder with the file DisplayCount.js
as follows:
import React from 'react';
import useOrderCountHook from '../hooks/useOrderCount';
function DisplayCount() {
const orderHook = useOrderCountHook();
return (
<div>
<h1>count:{orderHook.orderCount}</h1>
<button type='button' onClick
{orderHook.incrementOrderCount}>Increment</button>
<button type='button' onClick
{orderHook.decrementOrderCount}>Decrement</button>
</div>
);
}
export default DisplayCount;
Update the app.js component as follows:
import React from 'react';
import DisplayCount from './components/useOrderCount';
import './App.css';
function App() {
return (
<div>
<DisplayCount/>
</div>
);
}
export default App;
Running the app on the browser we should be able to increment and decrement the order count.
useLocalStorageState hook
our custom hook **useLocalStorageState**
will store the user’s age in local storage, update and gets it value from the localStorage.
Navigate to the hooks folder and create useLocalStorageState.js
file with the following
import React, { useState} from 'react'
function useLocalStorageState(key, defaultValue) {
const [state, setState] = useState(() => {
let value;
try {
value = JSON.parse(
window.localStorage.getItem(key) || String(defaultValue)
);
} catch (e) {
value = defaultValue;
}
return value;
});
useEffect(
() => {
window.localStorage.setItem(key, state);
}, [state]
)
return [state, setState];
}
export default useLocalStorageState;
Our useLocalStorageState
hook recieves two parameters (key, defaultValue). It uses the try block to retrieve the value of the key and assigns it to the variable value
and catch block assigns the defaultValue to the value
variable if an error occurs. The useEffect
hook sets, and update the key with the value of state
the parameters changes.
Using the useLocalStorageState
hook in other components:
In the component folder create DisplayLocalStorageData.js
with the following:
import React, { useState} from 'react'
import useLocalStorageState from '../hooks/useLocalStorageState'
function DisplayLocalStorageData () {
const [userAge, setUserAge] = useLocalStorageState("age",20);
return (
<div>
<h1>{userAge}</h1>
<button onClick={() => setUserAge(userAge + 1)}>Increment</button>
<button onClick={() => setUserAge(userAge - 1)}>Decrement</button>
</div>
)
}
export default DisplayLocalStorageData;
In order to use the useLocalStorageState
we just need to call it with the key and a default value. And the key to localStorage is added with the default value and fetched by our hook while the count is updated by the component itself. all the piece of code that interact with the localstorage is in our useLocalStorageState
hook while the state updating part is in the DisplayLocalStorageData
component.
Another great advantage that we have here is efficient code reusability across multiple components. Because we can reuse this same piece of code ( useLocalStorageState
hook) in any other component that needs to store it state in the local storage.
Creating global state Hooks
Most times when we build web applications, we often access the global state.
In a typical blog, some components need the comments state while some need the posts state. To abstract this functionality, we can create custom Hooks to get certain parts of the state:
First we take advantage of the context API to create a global state object that can be accessible from any component irrespective of how deeply nested the component might be.
// contexts.js
import React from 'react'
export const StateContext = React.createContext({
state: {
comments: [],
posts: []
},
})
Next, we create a useCommentState
hook that will return all the comments from the global state
// useCommentState.js
import { useContext } from 'react'
import { StateContext } from './contexts'
export default function useCommentState () {
const { state } = useContext(StateContext)
return state.comments
}
Similarly, we can create a usePostState
hook that will return all the posts from the global state.
// usePostsState.js
import { useContext } from 'react'
import { StateContext } from './contexts'
export default function usePostsState () {
const { state } = useContext(StateContext)
return state.posts
}
Using the useCommentState
hook in other components:
import React, { useState} from 'react'
import useCommentState from './useCommentState'
function DisplayComments () {
const comments = useCommentState();
return (
<div>
{comments.length > 0 ? (
comments.map( (comment, idx) => (<h4 key={idx}>{comment}</h4>
) : ( <h4>No comment</h4> )
}
</div>
)
}
export default DisplayComments;
Conclusion
In this article, we have learned how to make reusable piece of codes and also share it across multiple components by building our custom react hooks and using it in other components. This makes our codebase more readable and clean. For more information on react hooks, visit the official react documentation.