React Hooks

Esra Nur Mülkpınar
Bursa Bilişim Topluluğu
7 min readJun 7, 2024

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:

  1. What are Hooks?
  2. Why Do We Use Hooks?
  3. useState Hook
  4. useEffect Hook
  5. useContext Hook
  6. useDispatch Hook
  7. useSelector Hook
  8. useReducer Hook
  9. useCallback Hook
  10. useMemo Hook
  11. Custom Hooks
  12. 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

--

--