React Hooks enable you to use state and other React features without using class components in functional components. With React 16.8, state and side effects can be managed more concisely and simply.Learn More
There are several problems that React Hooks solve, such as
There are several built-in hooks in React, such as
UseState hooks provide an easy way to maintain state in functional components. A state value is passed in as an input, and then an array with two elements is returned: the current state value and a function to update it. In contrast, the useReducer hook is better suited to managing complex state logic. Like Redux, it takes a reducer function and an initial state, and returns a dispatch function to update the current state.
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
import React, { useReducer } from 'react';
const initialState = { count: 0 };
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
};
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const increment = () => {
dispatch({ type: 'increment' });
};
const decrement = () => {
dispatch({ type: 'decrement' });
};
return (
<div>
<p>Count: {state.count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
React lets you create custom hooks by extracting reusable stateful logic into a separate function and prefixing it with "use". These hooks can be used by other built-in hooks as well as custom hooks. You can use them to encapsulate and share common logic between multiple application components.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const json = await response.json();
setData(json);
setLoading(false);
} catch (error) {
console.error('Error fetching data:', error);
}
};
fetchData();
}, [url]);
return { data, loading };
}
// Another Component
import React from 'react';
import useFetch from './useFetch';
function MyComponent() {
const { data, loading } = useFetch('https://api.example.com/data');
if (loading) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Data:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
React hooks must follow three important rules:
React Hooks use the useEffect hook to handle side effects. It can be used for obtaining data from an API, subscribing to events, and manipulating DOM elements. In useEffect, the first parameter is a callback function that specifies what should happen when the component renders or when certain dependencies change.
// Example
useEffect(() => {
// Side effect code goes here
return () => {
// Cleanup code goes here (optional)
};
}, [dependencies]);
A useEffect hook is used in functional components to perform side effects. In class components, it combines the functionality of componentDidMount, componentDidUpdate, and componentWillUnmount. You can handle asynchronous actions, interact with external APIs, and update the DOM after a component has rendered using useEffect.
// Example - Fetching data using useEffect
import React, { useEffect, useState } from 'react';
const MyComponent = () => {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
};
fetchData();
}, []);
return (
<div>
{data.map(item => (
<p key={item.id}>{item.name}</p>
))}
</div>
);
};
UseEffect hooks accept an optional dependency array as a second argument. You can specify the dependencies that the effect relies on. The effect will run again if any of the dependencies change. An empty dependency array will only run the effect once after initial rendering. It facilitates performance optimization by preventing unnecessary re-renders.
import React, { useState, useEffect } from 'react';
const ExampleComponent = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Effect triggered!');
document.title = {count};
}, [count]);
const increment = () => {
setCount(prevCount => prevCount + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
export default ExampleComponent;
UseCallback hooks are used to memoize React functions. The memoized callback function is only changed if one of the dependencies changes. It can be useful to optimize performance when passing callbacks as props since it prevents the re-rendering of child components.
import React, { useState, useCallback } from 'react';
const ExampleComponent = () => {
const [count, setCount] = useState(0);
// Define the callback function using useCallback
const handleClick = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
};
export default ExampleComponent;
In React, the useMemo hook is used to memoize expensive computations. The function takes two arguments: a dependency array and a function. Whenever one of the variables changes, the function is re-evaluated. UseMemo is useful when you need to avoid calling a computation-intensive function every time you render.
import React, { useMemo } from 'react';
function ExampleComponent({ value1, value2 }) {
const result = useMemo(() => {
// Perform a computationally intensive calculation
let sum = 0;
for (let i = 1; i <= value1; i++) {
sum += i;
}
return sum * value2;
}, [value1, value2]);
return <div>Result: {result}</div>;
}
Based on value1 and value2, a computationally intensive calculation is performed to compute the result. Value1 and value2 are calculated only when the value changes.You use the useContext hook to consume values from the React context. Functional components can access context values directly without having to use context consumers. A context object can be used in a component by passing its current value as an argument to useContext.
import React, { useContext } from 'react';
// Create a Context
const MyContext = React.createContext();
// Create a parent component that provides the Context value
const ParentComponent = () => {
const data = 'Hello, useContext!';
return (
<MyContext.Provider value={data}>
<MyComponent />
</MyContext.Provider>
);
};
// Create a component that consumes the Context
const MyComponent = () => {
// Use the useContext hook to consume the Context
const contextData = useContext(MyContext);
return (
<div>
<h1>{contextData}</h1>
</div>
);
};
// Render the ParentComponent
ReactDOM.render(<ParentComponent />, document.getElementById('root'));
When using React Hooks, you can optimize performance by:
UseRef creates mutable references that persist across components renders. It gives you access to the underlying DOM element or allows you to store mutable values without re-rendering. For example, useRef is commonly used for accessing DOM elements, managing focus, or storing previous values to compare with the current ones.
import React, { useRef } from 'react';
function ExampleComponent() {
const inputRef = useRef(null);
const handleClick = () => {
// Accessing the input element using the ref
inputRef.current.focus();
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={handleClick}>Focus Input</button>
</div>
);
}
In state management, the useReducer hook is an alternative to useState. As arguments, it takes a reducer function and an initial state value and returns the current state along with a dispatch function. Reducers can be useful for state transitions that rely on previous states or for state logic that becomes too complex for useState.
import React, { useReducer } from 'react';
const initialState = 0;
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
case 'reset':
return initialState;
default:
return state;
}
};
const Counter = () => {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
};
export default Counter;
A class component is based on an ES6 class and manages state and side effects through a lifecycle method. JavaScript functional components with hooks manage state and side effects through hooks such as useState, useEffect, and others. In addition to encouraging code reuse, hooks provide a more concise and flexible way to manage state.
A testing framework such as Jest or React Testing Library can be used to test components that use hooks. By simulating user interactions, mocking API calls, and asserting that the component behaves as expected, you can verify the components behavior. It is possible to test hooks by making sure that state updates accurately, side effects are triggered correctly, and that they render the expected output.
Here are some best practices for using React Hooks:
In most cases, you would use useState to track a value that can change over time, which affects your component's rendering. In useState, a pair of values are returned: the current state value and a function to update it. Each time the state is updated, it triggers a re-render of the component.
In contrast, useRef is used primarily to hold mutable values across component renders without having to re-render. Using it, you can store any value as a mutable reference. The value of a useRef is not re-rendered when it is updated, unlike the value of a useState.