React Hooks are a feature that allows you to use state and other React capabilities in functional components. They are beneficial because they simplify code by eliminating the need for class components, making it easier to read and maintain. Hooks also enable more direct control over component state and side effects, resulting in more predictable and simpler code.
When using hooks, there are three essential rules to follow:
they can only be called inside React function components
they must be called at the top level of the component, and
they cannot be called conditionally.
These rules ensure that hooks behave consistently and correctly. Additionally, React allows you to create custom hooks also if you need, which enables you to encapsulate and reuse stateful logic across multiple components, enhancing code reusability and modularity. Custom hooks make it easier to share logic without repeating code, keeping your components clean and focused.
In this article, I will explain to you the top 6 most important and used React Hooks which you must know to use React’s maximum potential in your project/app. Every React Developer must know these hooks and how to use them.
useState
One of the most simple and used hooks of all, useState is a hook that lets you manage states(data or properties) in a function component in your application.
The useState
Hook can be used to keep track of strings, numbers, booleans, arrays, objects, and any combination of these!
useState
accepts an initial state and returns two values:
The current state.
A function that updates the state.
Initialization and Usage
import { useState } from "react";
function App(){
const [open, setOpen] = useState(false); // initial state we set false
return (
<>
<button
type="button"
onClick={() => setOpen(true)}
>toggle</button>
</>
)
}
open was the current state which we initialized as false, and setOpen was the function that we used in the button to change the state to “true”.
useEffect
useEffect
is a React Hook that lets you synchronize a component with an external system. It allows you to perform side-effects in your components like fetching data, directly updating the DOM, and timers.
useEffect
accepts two arguments with the 2nd argument being optional.
Setup.
Dependencies (optional).
Initialization and Usage
import { useState, useEffect } from 'react';
function ExampleComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
export default ExampleComponent;
In this example, useEffect
is used to update the document's title whenever the component renders. Each time you click the button, setCount
updates the count
state, causing the component to re-render, and useEffect
runs again, updating the document title to reflect the new count. This is not what we want. There are several ways to control when side effects run.
We should always include the second parameter which accepts an array. We can optionally pass dependencies to useEffect
in this array.
- No dependency
useEffect(() => {
//Runs on every render
});
2. With Dependency of Empty Array
useEffect(() => {
//Runs only on the first render
}, []);
3. With Props or state values:
useEffect(() => {
//Runs on the first render
//And any time any dependency value changes
}, [prop, state]);
Effect Cleanup
Some effects require cleanup to reduce memory leaks. Timeouts, subscriptions, event listeners, and other effects that are no longer needed should be disposed of. We do this by including a return function at the end of the useEffect
Hook.
import React, { useState, useEffect } from 'react';
function ExampleComponent() {
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
useEffect(() => {
// Function to update state when window is resized
const handleResize = () => {
setWindowWidth(window.innerWidth);
};
// Add event listener for window resize
window.addEventListener('resize', handleResize);
// Cleanup function to remove the event listener
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // Empty dependency array means this effect runs once on mount and cleanup on unmount
return (
<div>
<p>Window width: {windowWidth}px</p>
</div>
);
}
export default ExampleComponent;
In this example:
useEffect
sets up an event listener for the window'sresize
event.The
handleResize
function updates thewindowWidth
state with the current window width.The cleanup function returned
useEffect
removes theresize
event listener when the component unmounts or before the effect runs again, preventing potential memory leaks.
This pattern ensures that your component cleans up any resources it uses when it is no longer needed.
useRef
useRef
is a React Hook that lets you reference a value that’s not needed for rendering. The useRef
Hook allows you to persist values between renders. It can be used to store a mutable value that does not cause a re-render when updated. It can be used to access a DOM element directly.
useRef takes one argument and returns an object with a single property:
Argument:
initialValue
: The value you want the ref object’scurrent
property to be initially. It can be a value of any type. This argument is ignored after the initial render.Returns :
current
: Initially, it’s set to theinitialValue
you have passed. You can later set it to something else. If you pass the ref object to React as anref
attribute to a JSX node, React will set itscurrent
property. On the next renders,useRef
will return the same object.
Initialization and Usage
import React, { useRef, useEffect } from 'react';
function ExampleComponent() {
// Create a ref with an initial value of null
const inputRef = useRef(null);
// Using useEffect to focus the input element when the component mounts
useEffect(() => {
// Focus the input element
inputRef.current.focus();
}, []); // Empty dependency array ensures this effect runs only once on mount
const handleButtonClick = () => {
// Log the current value of the input element
console.log(inputRef.current.value);
};
return (
<div>
{/* Attach the ref to the input element */}
<input ref={inputRef} type="text" placeholder="Type something" />
<button onClick={handleButtonClick}>Log Input Value</button>
</div>
);
}
export default ExampleComponent;
useRef(null)
creates a ref object withcurrent
property initially set tonull
.The
useEffect
hook runs once after the initial render, focusing the input element by accessinginputRef.current
.The
ref
attribute on the<input>
element assigns the DOM element toinputRef.current
after the component mounts.When the button is clicked,
handleButtonClick
logs the current value of the input field by accessinginputRef.current.value
.
useContext
This Hook is a state management tool basically, it helps you to manage your states globally throughout your application. It can be used together with the useState
Hook to share state between deeply nested components more easily than with useState
alone.
It simplifies the process of consuming context values, providing an alternative to the traditional method of using the Context.Consumer
component.
What useContext
Does:
Accesses Context:
useContext
takes a context object (the value returned fromReact.createContext
) as an argument and returns the current context value for that context. This allows you to read the context value directly in your component.Simplifies Code: By using
useContext
, you avoid the need for nestedContext.Consumer
components, leading to cleaner and more readable code.
Why useContext
is Important:
State Sharing: It facilitates state and data sharing across the component tree without having to pass props down manually at every level. This is especially useful for global settings like themes, user authentication, and localization.
Improved Readability: The hook provides a more concise and readable way to consume context, reducing boilerplate and making the code easier to understand and maintain.
Functional Components: It enables functional components to easily access context values, promoting the use of modern React features and hooks over class components.
Initialization and Usage
- Create a Context
import React, { createContext } from 'react';
// Create a context with a default value
const ThemeContext = createContext('light');
export default ThemeContext;
2. Provide a Context Value
import React from 'react';
import ThemeContext from './ThemeContext';
import ThemedButton from './ThemedButton';
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
);
}
export default App;
3. Consume the Context Value
import React, { useContext } from 'react';
import ThemeContext from './ThemeContext';
function ThemedButton() {
// Use the useContext hook to access the current context value
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme === 'dark' ? '#333' : '#FFF', color: theme === 'dark' ? '#FFF' : '#000' }}>
{`Current theme is ${theme}`}
</button>
);
}
export default ThemedButton;
Step 1: We create a context using
createContext
from React. In this example, we create a context for the theme with a default value of'light'
.Step 2: In our
App
component, we wrap the component tree that needs access to the theme context withThemeContext.Provider
. We provide a value'dark'
to override the default theme.Step 3: In the
ThemedButton
component, we use theuseContext
hook to access the current theme value from theThemeContext
. We can then use this value to style the button accordingly.
useMemo
useMemo
is a React Hook that lets you cache the result of a calculation between re-renders. It memoizes a value so it doesn’t need to be recalculated. The useMemo
Hook only runs when one of its dependencies updates improving performance.
The useMemo
Hook can be used to keep expensive, resource-intensive functions from needlessly running.
Initialization and Usage
Let's understand this hook by first seeing the code with this problem and then how useMemo fixes it
Problem:
import React, { useState } from 'react';
function HeavyComputationComponent() {
const [count, setCount] = useState(0);
// A heavy computation function
const computeValue = () => {
let result = 0;
for (let i = 0; i < 100000000; i++) {
result += i;
}
return result;
};
const result = computeValue();
return (
<div>
<p>Result: {result}</p>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
</div>
);
}
export default HeavyComputationComponent;
In this example, computeValue
performs a heavy computation every time the component re-renders, even if the count
state hasn't changed. This can lead to performance issues, especially if the computation is complex or resource-intensive.
Solution using useMemo:
We can optimize the scenario with useMemo
hook to memoize the result of the computation.
import React, { useState, useMemo } from 'react';
function HeavyComputationComponent() {
const [count, setCount] = useState(0);
// A heavy computation function
const computeValue = () => {
let result = 0;
for (let i = 0; i < 100000000; i++) {
result += i;
}
return result;
};
// Memoize the result using useMemo
const result = useMemo(() => computeValue(), []);
return (
<div>
<p>Result: {result}</p>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
</div>
);
}
export default HeavyComputationComponent;
We wrap the heavy computation function
computeValue
withuseMemo
.The second argument
useMemo
is an array of dependencies. If any of these dependencies change,computeValue
will be recomputed; otherwise, it will return the memoized result.In this case, the empty dependency array
[]
ensures thatcomputeValue
is only computed once, during the initial render.As a result, the heavy computation is performed only when necessary, improving performance by avoiding unnecessary recalculations on each render.
useCallback
This hook is similar to what useMemo
does just here instead of value it returns a memoized function. It takes two arguments, one is the function and second are the dependencies for which you want the re-render to occur.
useCallback(fn, dependencies)
Initialization and Usage
We will understand this hook similar to useMemo with a problem code and then solving it with useCallback.
Problem:
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [count, setCount] = useState(0);
// Callback function passed to ChildComponent
const handleClick = () => {
console.log('Button clicked!');
};
return (
<div>
<ChildComponent onClick={handleClick} />
<button onClick={() => setCount(count + 1)}>Increment Count</button>
</div>
);
}
export default ParentComponent;
In this example, handleClick
is recreated on each render of ParentComponent
. Every time ParentComponent
re-renders, a new reference to handleClick
is created, causing the onClick
prop passed to ChildComponent
to change. This leads to unnecessary re-renders of ChildComponent
, even if its props haven't changed.
Solution using useCallback:
import React, { useState, useCallback } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [count, setCount] = useState(0);
// Memoize the callback function using useCallback
const handleClick = useCallback(() => {
console.log('Button clicked!');
}, []);
return (
<div>
<ChildComponent onClick={handleClick} />
<button onClick={() => setCount(count + 1)}>Increment Count</button>
</div>
);
}
export default ParentComponent;
We wrap the callback function
handleClick
withuseCallback
.The second argument of
useCallback
is an array of dependencies. If any of these dependencies change, the callback function will be recreated; otherwise, it will return the memoized function.In this case, the empty dependency array
[]
ensures thathandleClick
is only created once, during the initial render.As a result, the same callback function is passed to
ChildComponent
on each render ofParentComponent
, preventing unnecessary re-renders ofChildComponent
unless it's props or state change.
End Note
Well done! You have now understood the react hooks in-depth and also seen the most important hooks and how they work. Now What's left if for you to start implementing these in your React projects to use them to their full potential and make them best in terms of performance and functionality. Try and if you get stuck, come back here to get help.
Thank you for reading! If you have any feedback or notice any mistakes, please feel free to leave a comment below. I’m always looking to improve my writing and value any suggestions you may have. If you’re interested in working together or have any further questions, please don’t hesitate to reach out to me at fa1319673@gmail.com.