React Hooks
Hooks are new addition in React 16.8. Hooks are a fundamentally simpler way to encapsulate stateful behavior and side effects in user interfaces. In simple words they let you use state and other react feature without writing a class. They were first introduced in React and have been broadly embraced by other frameworks like Vue, Svelte, and even adapted for general functional JavaScript. However, their functional design requires a good understanding of closures in JavaScript.
What is hooks:-
Hooks are functions that let you “hook into” React state and life cycle features from function components. Hooks don’t work inside classes, they let you use React without classes.
- State hooks:
useState is a hooks, we call it inside a function component to add some local state to it. React will preserve this state between re-renders. In addition useState returns a pair: the current state value and a function that lets you update it. You can call this function from an event handler or somewhere else. It’s similar to this.setState in a class, except it doesn’t merge the old and new state together.
The useState accepts one parameter that is initial state. You can declare multiple state hook in same component.
const [count, setCount] = useState (0);
In above example “count” is the state and “0” is the initial value of the state, “setCount” is the function that allow us to change/set state in react component.
- Effect hooks:
You’ve likely performed data fetching, subscriptions, or manually changing the DOM from React components before. We call these operations “side effects” (“effects” for short) because they can affect other components and can’t be done during rendering. The Effect Hook, useEffect, adds the ability to perform side effects from a function component. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in React classes, but unified into a single API.
In below example we used state hooks as well as effect hooks, in this example “cartItem” is the state and “handleButtonClick” is the function that update the state. The useEffect is act as componentDidMount, componentDidUpdate, and componentWillUnmount, when the DOM first renders the useEffect is called and in this we add event listeners to mouse position, when the component re-renders useEffect is called again, we will pass return inside useEffect this will calls when the component will remove from the DOM.
import React, { useState, useEffect } from 'react';
import './App.css';
function App() {
const [cartItems, handleButtonClick] = useState([]);
const [coordinateX, setXcoordinate] = useState(0);
const [coordinateY, setYcoordinate] = useState(0);
const setMousePosition = e => {
const coordinateX = e.clientX;
const coordinateY = e.clientY;
setXcoordinate(coordinateX);
setYcoordinate(coordinateY);
}
useEffect(() => {
document.addEventListener('mousemove', setMousePosition);
return () => {
document.removeEventListener('mousemove', setMousePosition);
}
}, [cartItems]);
return (
<div className="App">
<div>
Cart : {cartItems.toString()}
<br /> X: {coordinateX} Y: {coordinateY}
</div>
<button onClick={() => { handleButtonClick([...cartItems, 'Item']) }}>Add To Cart</button>
</div>
);
}
export default App;
Rules of React hooks:-
Before we dive deep into the usages of hooks, we need to understand the rules of React Hooks.
1. Only call hooks at top level :
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. Do not call hooks in class component, event handler or inside function passed to useReducer, useMemo or useEffect.
2. Only call hooks from React functions :
Don’t call Hooks from regular JavaScript functions. Instead, you can call Hooks from React function components or you can call hooks from custom hooks.
- Below is the example for call hooks from React function
import React, { useState } from 'react';
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick ={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
we use functional component but we also import React into the file, this is what makes it possible for us to use things like JSX and React hooks inside.
- Below is the example for call hooks from custom hooks
export default function useUserName(userName) {
const [isPresent, setIsPresent] = useState(false);
useEffect(() => {
const data = MockedApi.fetchData();
data.then((res) => {
res.forEach((e) => {
if (e.name === userName) {
setIsPresent(true);
}
});
});
});
return isPresent;
}
In this example we created a generic component useUserName using custom hooks. For creating custom hooks create a JavaScript function whose name starts with “use” and that may call other Hooks. Now we can use this function where ever we want, like below example:
function FriendActive(props) {
const isActive = useFriendStatus(props.friend.id);
return isActive ? 'Online' : 'Offline';
}
Best practices of Hooks:-
1. Organize and structure your hooks:
One of the advantages of React Hooks is the ability to write less code that is easy to read. In some cases, the amount of useEffect() and useState() can still be confusing. When you keep your component organized it will help in readability and keep the flow of your components consistent and predictable. If your custom Hooks are too complicated, you can always break them down to sub-custom Hooks. Extract the logic of your component to custom Hooks to make your code readable.
2. Use custom hooks:
There might be instances where you have been using the same repetitive and redundant stateful logic inside multiple components. We were able to handle this situation by relying on Render props and Higher Order Components. But with hooks, we can do it in a much simpler and cleaner way, i.e., by using custom hooks. Instead of writing repetitive logic in two different component using hooks, write it one component based on respective scenarios and use the custom hook component wherever you want.
3. Keep your hooks simple:
Keeping React Hooks simple will give you the power to effectively control and manipulate what goes on in a component throughout its lifetime. Avoid writing custom Hooks as much as possible when there is no repetitive code, you can inline a useState() or useEffect() instead of creating your own hook.
If your code have most of the repetitive code and if you create bunch of custom hooks for functionality, you can create a custom hook that acts as a wrapper for these. Below is the example for this:
Code snippet 1:
function {
useHook(...);
useHook(...);
useHook(...);
return (
<div>...</div>
);
}
Code snippet 2:
function {
useCustomHook(...);
useHook(...);
useHook(...);
return (
<div>...</div>
);
}
In the above code snippets the code snippet 2 is the best version, because it keeps the hook simple and all other useHooks are inline accordingly. This allows us to create functionality that can be reused across different components and also gives us more power to control and manipulate our components effectively, it make our component generic you can use useCustomHook component wherever you want in your code, instead of snippet 1 you have to repeat the same logic in other components.
4. Avoid calling useEffect on every re-rendering:
Always avoid unnecessary re-rendering and calling useEffect hooks in your application for that react give very unique strategy in hooks that act same as “shouldComponentUpdate” method in class component, i.e., second parameter in useEffect.
The second parameter in useEffect hook accept an array that allow react to call useEffect only when props or state array that you add in this array is changed.
import React, { useState, useEffect } from 'react';
import './App.css';
function App() {
const [cartItems, handleButtonClick] = useState([]);
const [coordinateX, setXcoordinate] = useState(0);
const [coordinateY, setYcoordinate] = useState(0);
const setMousePosition = e => {
const coordinateX = e.clientX;
const coordinateY = e.clientY;
setXcoordinate(coordinateX);
setYcoordinate(coordinateY);
}
useEffect(() => {
document.addEventListener('mousemove', setMousePosition);
return () => {
document.removeEventListener('mousemove', setMousePosition);
}
}, [cartItems]);
return (
<div className="App">
<div>
Cart : {cartItems.toString()}
<br /> X: {coordinateX} Y: {coordinateY}
</div>
<button onClick={() => { handleButtonClick([...cartItems, 'Item']) }}>Add To Cart</button>
</div>
);
}
export default App;
In below example we added useEffect hooks that only called when the “cartItems” state is changes. If we want to call useEffect only once when the component is first re-renders in this we have to pass empty array ([]) in the second parameter of hooks. That allow us to call useEffect as componentDidMount that calls only one time when component is first rendered.
Conclusion
All in all I am really enjoying React hooks and the benefits they are bringing for making my code much more readable and maintainable. We’ve learned the two most important rules of React Hooks and how to effectively think in Hooks.
Sources:
Introduction of Hooks: https://reactjs.org/docs/hooks-intro.html
Best Practices of Hooks: https://www.smashingmagazine.com/2020/04/react-hooks-best-practices/
Advantages of React Hooks: https://medium.com/@mateuszroth/react-hooks-advantages-and-comparison-to-older-reusable-logic-approaches-in-short-f424c9899cb5#:~:text=React%20Hooks%20advantages,coupled%20in%20a%20custom%20hook.
Thanks Arpit, this helped me a lot.
Great Arpit Nagpure Keep going..
Great article arpit!! Keep it up!!