React Hooks | All in one

Introduction

React hooks are functions that allow you to use state and other React features in functional components. They enable you to manage component lifecycle events, side effects, and context without needing to write class components. Here’s an overview of the most commonly used React hooks along with small examples for each.

1. useState

The useState hook lets you add state to functional components.

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>
);
}

export default Counter;

2. useEffect

The useEffect hook lets you perform side effects in function components, such as data fetching, subscriptions, or manually changing the DOM.

import React, { useState, useEffect } from 'react';

function Example() {
const [count, setCount] = useState(0);

useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}

export default Example;

 

3. useContext

The useContext hook allows you to access the context values directly without needing to wrap components in the Context.Consumer.

import React, { useContext } from 'react';

const ThemeContext = React.createContext('light');

function ThemedButton() {
const theme = useContext(ThemeContext);

return (
<button style={{ background: theme === 'dark' ? '#333' : '#FFF' }}>
{theme === 'dark' ? 'Dark Theme' : 'Light Theme'}
</button>
);
}

function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
);
}

export default App;

 

4. useReducer

The useReducer hook is an alternative to useState for managing complex state logic in components. It is particularly useful when the next state depends on the previous one.

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 (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</>
);
}

export default Counter;

 

5. useRef

The useRef hook allows you to create mutable references that persist for the full lifetime of the component. It can be used to directly access DOM elements.

import React, { useRef } from 'react';

function FocusInput() {
const inputRef = useRef(null);

const focusInput = () => {
inputRef.current.focus();
};

return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus the input</button>
</div>
);
}

export default FocusInput;

 

5. useMemo

The useMemo hook returns a memoized value. It is useful for optimizing performance by memoizing expensive calculations.

import React, { useState, useMemo } from 'react';

function Example() {
const [count, setCount] = useState(0);

const expensiveCalculation = (num) => {
console.log('Calculating...');
return num * 2;
};

const memoizedValue = useMemo(() => expensiveCalculation(count), [count]);

return (
<div>
<p>Count: {count}</p>
<p>Memoized Value: {memoizedValue}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

export default Example;

 

7. useCallback

The useCallback hook returns a memoized callback function, which can help prevent unnecessary re-renders of child components.

import React, { useState, useCallback } from 'react';

function Button({ handleClick }) {
console.log('Rendering button');
return <button onClick={handleClick}>Click me</button>;
}

function Example() {
const [count, setCount] = useState(0);

const handleClick = useCallback(() => {
console.log('Button clicked');
}, []); // Only recreate the function if dependencies change

return (
<div>
<p>Count: {count}</p>
<Button handleClick={handleClick} />
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

export default Example;

 

8. useLayoutEffect

The useLayoutEffect hook is similar to useEffect, but it runs synchronously after all DOM mutations. It is useful for reading layout from the DOM and synchronously re-rendering.

import React, { useLayoutEffect, useRef } from 'react';

function LayoutEffectExample() {
const divRef = useRef(null);

useLayoutEffect(() => {
console.log('Div height:', divRef.current.offsetHeight);
});

return <div ref={divRef}>Hello, world!</div>;
}

export default LayoutEffectExample;

 

9. useDebugValue

The useDebugValue hook is used to display a label for custom hooks in React DevTools. It helps to provide a debug value that can show useful information about the hook.

import React, { useDebugValue, useState } from 'react';

function useCustomHook(value) {
useDebugValue(value ? 'Value is present' : 'No value');
return value;
}

function DebugExample() {
const [value, setValue] = useState(false);
useCustomHook(value);

return (
<div>
<button onClick={() => setValue(!value)}>
Toggle Value
</button>
</div>
);
}

export default DebugExample;

 

10. useImperativeHandle

The useImperativeHandle hook customizes the instance value that is exposed to parent components when using ref. It is typically used in conjunction with React.forwardRef to allow parent components to interact with child components more easily.

import React, { useImperativeHandle, forwardRef, useRef } from 'react';

const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef();

useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
getValue: () => {
return inputRef.current.value;
},
}));

return <input ref={inputRef} type="text" />;
});

function ParentComponent() {
const inputRef = useRef();

const handleFocus = () => {
inputRef.current.focus();
};

const handleGetValue = () => {
alert(inputRef.current.getValue());
};

return (
<div>
<CustomInput ref={inputRef} />
<button onClick={handleFocus}>Focus Input</button>
<button onClick={handleGetValue}>Get Input Value</button>
</div>
);
}

export default ParentComponent;

 

Two important hooks from Redux

11. useSelector (from Redux)

The useSelector hook is used to extract data from the Redux store state. It allows you to subscribe to the Redux store and access its state within functional components.

import React from 'react';
import { useSelector } from 'react-redux';

function Counter() {
// Use useSelector to get the count value from the Redux store
const count = useSelector((state) => state.counter.value);

return (
<div>
<p>Count: {count}</p>
</div>
);
}

export default Counter;

 

12. useDispatch (from Redux)

The useDispatch hook returns a reference to the dispatch function from the Redux store. You can use it to dispatch actions to the store.

import React from 'react';
import { useDispatch } from 'react-redux';
import { increment, decrement } from './counterSlice'; // Assuming you have a slice for your counter

function CounterControls() {
const dispatch = useDispatch();

return (
<div>
<button onClick={() => dispatch(increment())}>Increment</button>
<button onClick={() => dispatch(decrement())}>Decrement</button>
</div>
);
}

export default CounterControls;

 

Summary of React Hooks

  • useState: Manage state in functional components.
  • useEffect: Handle side effects like data fetching or subscriptions.
  • useContext: Access context directly in components.
  • useReducer: Manage complex state logic.
  • useRef: Create mutable references to DOM elements.
  • useMemo: Memoize expensive calculations.
  • useCallback: Memoize callback functions to prevent re-renders.
  • useLayoutEffect: Handle layout effects synchronously after DOM mutations.
  • useDebugValue: Display debug information for custom hooks in DevTools.
  • useImperativeHandle: Customize the ref instance that is exposed to parent components, allowing them to access specific functions or properties from the child component.
  • useSelector: Extract data from the Redux store, allowing functional components to subscribe to the Redux state.
  • useDispatch: Access the dispatch function from the Redux store, enabling you to dispatch actions to update the state.

These hooks provide powerful tools for building functional components in React, improving code organization and performance. Feel free to ask if you have any questions or need further clarification on any of the hooks!

Custom hooks

Custom hooks in React are a way to extract and reuse logic between components.

Reusability

Custom hooks allow you to reuse logic across different components. Instead of duplicating the same logic (like fetching data) in multiple components, you can create a custom hook that can be shared.

  • Example: If multiple components need to fetch data from different APIs, you can use the same useFetch hook with different URLs.

Let’s create the Custom Hook to fetch data.

import { useState, useEffect } from 'react';

const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error(`Error: ${response.status}`);
        }
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

export default useFetch;

Usage of Custom hook

import React from 'react';
import useFetch from './useFetch'; // Assume it's in the same directory

const UsersList = () => {
  const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <div>
      <h1>Users List</h1>
      <ul>
        {data.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default UsersList;

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Articles