React Hooks
Hello dear developer friends, if the coffees are ready, so are we. 🚀 Today, our topic is React Hooks, which entered our lives with React 16.8. Happy reading! 👩🏻💻
✨ Topics covered in this article:
- What are Hooks?
- Why Do We Use Hooks?
- useState Hook
- useEffect Hook
- useContext Hook
- useDispatch Hook
- useSelector Hook
- useReducer Hook
- useCallback Hook
- useMemo Hook
- Custom Hooks
- Things to Keep in Mind When Using Hooks
✨ What are Hooks?
React Hooks are special functions used for state and side effect management. They also allow you to create your own Hooks. Hooks enable the use of state and other React features in functional components without the need for class components.
✨ Why Do We Use Hooks?
Hooks enhance React’s flexibility and power, allowing you to write more readable, reusable, and maintainable code. With Hooks, we can manage state and lifecycle methods in functional components.
✨ useState Hook
useState is the most basic Hook for state management in React. It defines a component’s state and returns a function to update this state.
Usage Example:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
In this example, the useState Hook defines a state variable named count
and a function named setCount
to update this state. Each time the button is clicked, the setCount
function is used to increment the count
value.
✨ useEffect Hook
useEffect is used to manage side effects. It allows you to run certain pieces of code during the mount, update, and unmount phases of components.
Mount: A component is added to the DOM for the first time. During this phase, the component’s initial state is set, and the first render occurs.
Update: A component re-renders due to changes in props or state. During this phase, the component is re-rendered with updated data.
Unmount: A component is removed from the DOM. During this phase, any cleanup related to the component is performed.
Usage Example:
import React, { useEffect, useState } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(seconds => seconds + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <div>{seconds} seconds have passed.</div>;
}
In this example, the useEffect Hook creates a timer. When the component is first rendered (mount), a setInterval
function runs to increment the seconds
value every second. When the component unmounts, the timer is cleared with clearInterval
.
✨ useContext Hook
The useContext
hook allows sharing data between components using React's context API.
Usage Example:
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme === 'dark' ? '#333' : '#FFF' }}>
I am styled by theme context!
</button>
);
}
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
);
}
In this example, the useContext
hook is used to get the theme information from ThemeContext
and adjust the background color of the button accordingly.
✨ useDispatch Hook
The useDispatch
hook is used to dispatch actions to the Redux store. Actions are operations that update the state in the store.
Redux is a centralized state management library for JavaScript applications, commonly used with React. By keeping the entire state of an application in a single store, Redux makes state management more predictable and manageable.
Usage Example:
In this example, the useDispatch
hook is used to dispatch the increment
action. When the button is clicked, the increment
action is sent to the Redux store, and the state is updated.
✨ useSelector Hook
The useSelector
hook is used to select state from the Redux store and use it within a component. This hook allows components to get the state data they need.
Usage Example:
import React from 'react';
import { useSelector } from 'react-redux';
function Counter() {
const count = useSelector((state) => state.counter.value);
return (
<div>
<p>Count: {count}</p>
</div>
);
}
In this example, the useSelector
hook is used to select the value
state from the counter
slice in the Redux store. The selected count
value is displayed within the component.
✨ useReducer Hook
The useReducer
hook is used for more complex state logic and state transitions. It provides Redux-like state management.
Usage Example:
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
In this example, a counter application is created using the useReducer
hook. The reducer
function manages state transitions, and these transitions are triggered using the dispatch
function.
✨ useCallback Hook
The useCallback
hook improves performance by preventing a function from being recreated unnecessarily. It is used to avoid unnecessary re-renders.
Usage Example:
import React, { useState, useCallback } from 'react';
function ExpensiveComponent({ calculate }) {
console.log('Rendering expensive component');
return <div>{calculate()}</div>;
}
function App() {
const [count, setCount] = useState(0);
const calculate = useCallback(() => {
return count * 2;
}, [count]);
return (
<div>
<ExpensiveComponent calculate={calculate} />
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
In this example, the useCallback
hook is used to memoize the calculate
function, recreating it only when count
changes. This prevents ExpensiveComponent
from re-rendering unnecessarily.
✨ useMemo Hook
The useMemo
hook memorizes a value, recomputing it only when its dependencies change. It is used for performance optimization.
Usage Example:
import React, { useMemo, useState } from 'react';
function ExpensiveCalculationComponent() {
const [count, setCount] = useState(0);
const [inputValue, setInputValue] = useState('');
const expensiveCalculation = useMemo(() => {
console.log('Calculating...');
return count * 2;
}, [count]);
return (
<div>
<p>Result of expensive calculation: {expensiveCalculation}</p>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Type something..."
/>
</div>
);
}
export default ExpensiveCalculationComponent;
In this example, the useMemo
hook memoizes the expensiveCalculation
result, recomputing it only when count
changes. This prevents unnecessary recalculations and improves performance.
✨ Custom Hooks
Custom Hooks encapsulate application-specific logic and make it reusable. This makes your code more modular and clean.
Usage Example:
import { useState, useEffect } from 'react';
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return width;
}
function App() {
const width = useWindowWidth();
return <div>Window width: {width}</div>;
}
In this example, a custom hook named useWindowWidth
is created to track the window width and used in the App
component.
✨ Important Points When Using Hooks
There are some rules and best practices to follow when using React hooks and writing your own hooks. Here are some key points:
🚀 Custom Hook
1. Should Start with “use”:
When writing your own hook, its name should start with “use”. This tells React that it is a hook and that hook rules apply.
2. Should Only Be Used Inside Functions:
Hooks should only be used inside function components or other hooks.
3. Follow React Rules:
You should only call hooks at the top level, not inside conditions or loops. This allows React to track the order of hook calls.
4. Can Use Other Hooks:
You can use other hooks (useState, useEffect, etc.) within your custom hook. This increases the power and flexibility of hooks.
🚀 Performance Optimization
1. Dependency Lists:
Hooks, especially hooks like useEffect, useCallback, and useMemo, rely on dependency lists. Always specify the dependency list correctly.
2. Using Memoization:
Use useMemo and useCallback hooks to avoid recalculating expensive calculations or functions.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
🚀 Using State and Effect in Custom Hooks
Custom hooks can use built-in hooks like useState and useEffect to manage state and side effects. This helps custom hooks reduce the complexity of components.
🚀 Reusability and Abstraction
Custom hooks allow you to package specific logic or operations in a reusable and abstract way. This makes your code cleaner and more manageable.
🚀 Error Handling and Debugging
1. Handling Errors:
Ensure that your custom hooks properly handle errors. You can use try-catch blocks and state variables to manage error states.
2. Debugging:
You can debug your hooks using React DevTools. Use the features provided by React DevTools to track the state and effects of custom hooks.
🚀 Example Uses
1. Form Handling Custom Hook
You can create a custom hook for form handling.
import { useState } from 'react';
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const handleChange = (event) => {
const { name, value } = event.target;
setValues({
...values,
[name]: value,
});
};
return [values, handleChange];
}
export default useForm;
2. Fetching Data Custom Hook
You can create a custom hook for data fetching.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then((response) => response.json())
.then((data) => {
setData(data);
setLoading(false);
})
.catch((error) => {
setError(error);
setLoading(false);
});
}, [url]);
return { data, loading, error };
}
export default useFetch;
If you enjoyed the article, don’t forget to check out my other content. Happy reading! 📚
You can buy me a coffee to support me 🌟
If you want to get in touch with me: Esra Nur Mülkpınar