React Hooks
What are React Hooks?
React Hooks are special functions introduced in React 16.8 that allow developers to use state and other React features, such as lifecycle methods, in functional components. Hooks provide a way to manage component state, side effects, context, and more without using class components.
What is the useState
hook, and how is it used?
The useState
hook is a React Hook that allows you to add state to functional components. It returns an array with two elements: the current state value and a function to update that state. You can initialize state by passing an initial value to useState
.
Example of using useState
:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // Initialize state with 0
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
In this example, useState(0)
initializes the state variable count
to 0, and setCount
is used to update it when the button is clicked.
What is the useEffect
hook, and how does it work?
The useEffect
hook allows you to perform side effects in functional components, such as data fetching, subscriptions, or manually changing the DOM. It runs after the render and can optionally clean up effects when the component unmounts or when dependencies change. The hook accepts two arguments: a callback function and an optional dependency array.
Example of using useEffect
:
import React, { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []); // Empty array ensures the effect runs only once
return <div>Data: {JSON.stringify(data)}</div>;
}
In this example, the effect runs once after the component is mounted to fetch data from an API, and the fetched data is stored in the component's state.
What is the dependency array in useEffect
, and how does it work?
The dependency array is an optional second argument passed to useEffect
that tells React when to re-run the effect. If dependencies (state or props) in the array change, the effect will run again. If the array is empty, the effect will only run once after the initial render, similar to componentDidMount()
in class components.
Example with a dependency array:
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]); // The effect will run whenever `count` changes
In this example, the effect updates the document title every time the count
state changes.
How do you clean up effects in useEffect
?
If your effect creates side effects that need cleanup (e.g., timers, subscriptions), you can return a cleanup function from the useEffect
callback. The cleanup function is called when the component unmounts or before the effect is re-run (if dependencies change).
Example of cleaning up an effect:
useEffect(() => {
const timer = setInterval(() => {
console.log('Tick');
}, 1000);
return () => clearInterval(timer); // Cleanup on unmount or before re-running
}, []); // Run once
In this example, the timer is cleared when the component unmounts, preventing memory leaks.
What is the useContext
hook, and how is it used?
The useContext
hook allows functional components to consume values from a React Context. Context provides a way to pass data through the component tree without manually passing props down at every level. The useContext
hook makes it easy to access the context value inside a functional component.
Example of using useContext
:
const ThemeContext = React.createContext('light');
function ThemeComponent() {
const theme = useContext(ThemeContext); // Access context value
return <div>Current theme: {theme}</div>;
}
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemeComponent />
</ThemeContext.Provider>
);
}
In this example, useContext
is used to consume the theme value provided by the ThemeContext
.
What is the useReducer
hook, and how does it differ from useState
?
useReducer
is a hook that provides an alternative to useState
for managing more complex state logic. It is particularly useful when the state transitions depend on multiple actions or when the state is derived from previous state values. useReducer
takes a reducer function and an initial state as arguments, and it returns the current state and a dispatch function.
Example of using useReducer
:
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:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
In this example, useReducer
manages the count
state and updates it based on the dispatched actions ('increment'
and 'decrement'
).
What is the useMemo
hook, and when should you use it?
The useMemo
hook is used to memoize expensive computations, ensuring that a function only runs when its dependencies change. This can help improve performance by preventing unnecessary recalculations on every render. useMemo
takes a function and a dependency array as arguments.
Example of using useMemo
:
import React, { useState, useMemo } from 'react';
function ExpensiveCalculation({ count }) {
const expensiveResult = useMemo(() => {
console.log('Calculating...');
return count * 2;
}, [count]);
return <p>Result: {expensiveResult}</p>;
}
In this example, the expensive calculation is only re-run when the count
value changes, thanks to useMemo
.
What is the useCallback
hook, and how does it optimize performance?
useCallback
is a hook that memoizes a callback function, ensuring that the function reference remains the same between renders unless its dependencies change. This can optimize performance by preventing child components from re-rendering unnecessarily when the parent component re-renders.
Example of using useCallback
:
import React, { useState, useCallback } from 'react';
function Child({ onClick }) {
console.log('Child rendered');
return <button onClick={onClick}>Click me</button>;
}
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Button clicked');
}, []); // Memoize the function
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<Child onClick={handleClick} />
</div>
);
}
In this example, useCallback
prevents the handleClick
function from being re-created on every render, which optimizes the Child
component's performance.
What is the useRef
hook, and what are its common use cases?
The useRef
hook provides a way to access and persist a mutable reference that doesn't trigger a re-render when it changes. It is commonly used for directly accessing DOM elements, managing focus, or holding a value that persists across renders without causing re-renders.
Example of using useRef
to access a DOM element:
import React, { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus(); // Directly focus the input element
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus the input</button>
</div>
);
}
In this example, useRef
is used to store a reference to the input element, and the focusInput
function uses this reference to focus the input when the button is clicked.
What is the useLayoutEffect
hook, and how does it differ from useEffect
?
useLayoutEffect
is similar to useEffect
, but it fires synchronously after all DOM mutations and before the browser paints the screen. It can be used when you need to make DOM measurements or changes that should happen before the browser updates the layout, such as updating scroll positions or measuring elements.
Example of using useLayoutEffect
:
import React, { useLayoutEffect, useRef } from 'react';
function ScrollComponent() {
const divRef = useRef(null);
useLayoutEffect(() => {
const div = divRef.current;
div.scrollTop = div.scrollHeight; // Scroll to the bottom of the div
});
return <div ref={divRef} style={{ overflowY: 'scroll', height: '100px' }}>Content...</div>;
}
In this example, useLayoutEffect
ensures that the scroll position is updated before the browser paints the screen, preventing any visible "jumps" in the layout.
What is the useImperativeHandle
hook?
useImperativeHandle
allows you to customize the instance value that is exposed to parent components when using ref
. It is often used with forwardRef
to control what the parent component can access via a ref, instead of exposing the entire DOM element or child component.
Example of using useImperativeHandle
:
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus(); // Custom focus method
},
}));
return <input ref={inputRef} type="text" />;
});
function Parent() {
const inputRef = useRef();
return (
<div>
<FancyInput ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus the input</button>
</div>
);
}
In this example, useImperativeHandle
is used to expose only the focus
method from the FancyInput
component to the parent component via the ref
.