Hooks Code examples - rs-hash/Senior GitHub Wiki
import React, { useState } from "react";
function UseStateComp() {
const [count, setCount] = useState(0);
const addCount = () => {
setCount((prev) => prev + 1);
};
const removeCount = () => {
setCount((prev) => prev - 1);
};
return (
<div>
<h1>Count is {count}</h1>
<button onClick={addCount}>Add 1</button>
<button onClick={removeCount}>Remove 1</button>
</div>
);
}
export default UseStateComp;
useEffect: Enables side effects in functional components, such as data fetching, DOM manipulation, and subscriptions.
useEffect
is a React hook that allows you to perform side effects in functional components. Side effects may include data fetching, DOM manipulation, or subscribing to external data sources. useEffect
is similar to lifecycle methods in class components, but it's designed for use in functional components. It takes two arguments: a function that contains the code to run for the side effect and an optional array of dependencies that specify when the effect should run.
Here's a code example demonstrating how to use useEffect
to fetch data from an API when a component mounts:
import React, { useState, useEffect } from 'react';
function DataFetching() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Define an asynchronous function to fetch data
async function fetchData() {
try {
// Fetch data from an API (e.g., JSONPlaceholder)
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const jsonData = await response.json();
// Update the component's state with the fetched data
setData(jsonData);
setLoading(false); // Set loading to false when data is fetched
} catch (error) {
console.error('Error fetching data:', error);
setLoading(false); // Set loading to false in case of an error
}
}
// Call the fetchData function when the component mounts
fetchData();
}, []); // The empty dependency array means this effect runs only once on mount
return (
<div>
<h1>Data Fetching</h1>
{loading ? (
<p>Loading...</p>
) : (
<ul>
{data.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
)}
</div>
);
}
export default DataFetching;
useContext: Provides access to the context values defined in a higher-level component in the component tree.
useContext and the Context API are features in React that allow you to pass data (such as configuration settings, themes, or user authentication status) down the component tree without manually passing props through every level of nesting. This is particularly useful for managing global state or providing data that many components need access to.
Here's a brief explanation of useContext and the Context API in React:
Context API: The Context API is a part of React that provides a way to share values, such as state or functions, between components without having to explicitly pass them through props. It consists of two main parts: the createContext function and a Context.Provider component. You create a context using createContext, and then you wrap a part of your component tree with a Context.Provider to make the context available to components nested inside it.
useContext Hook: The useContext hook is a React hook that allows functional components to consume values from a context created with createContext. It simplifies accessing context values without needing to wrap components with Context.Consumer or passing props. You simply call useContext with the context object to access the value.
- First, ensure you have React and React DOM installed. If not, you can install them using npm or yarn:
npm install react react-dom
- Create your React context in a separate file, for example,
MyContext.js
:
import React, { createContext, useContext, useState } from 'react';
// Create a context object
const MyContext = createContext();
// Create a context provider component
export function MyProvider({ children }) {
const [name, setName] = useState('John Doe');
return (
<MyContext.Provider value={{ name, setName }}>
{children}
</MyContext.Provider>
);
}
// Create a custom hook to access the context
export function useMyContext() {
return useContext(MyContext);
}
- In your main application file, wrap your component tree with the
MyProvider
:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { MyProvider } from './MyContext'; // Import the context provider
ReactDOM.render(
<MyProvider>
<App />
</MyProvider>,
document.getElementById('root')
);
- Now, in your actual components, you can use the
useMyContext
hook to access the context:
import React from 'react';
import { useMyContext } from './MyContext'; // Import the custom hook
function MyComponent() {
const { name, setName } = useMyContext();
return (
<div>
<h1>Hello, {name}!</h1>
<button onClick={() => setName('New Name')}>Change Name</button>
</div>
);
}
export default MyComponent;
useRef
is a React Hook that provides a way to interact with DOM elements directly or to store mutable values that persist between renders without causing re-renders. It's commonly used for accessing and manipulating DOM elements and for preserving values that should not trigger component re-renders. Here's a code example to demonstrate how to use useRef
:
import React, { useRef, useEffect } from 'react';
function App() {
// Create a ref for a DOM element
const myButtonRef = useRef(null);
// Create a ref for a mutable value
const countRef = useRef(0);
// Function to handle a button click
const handleClick = () => {
// Access and manipulate the DOM element directly
if (myButtonRef.current) {
myButtonRef.current.innerHTML = 'Button Clicked!';
}
// Access and update a mutable value
countRef.current += 1;
console.log(`Button clicked ${countRef.current} times.`);
};
// Use useEffect to run code after render
useEffect(() => {
// Focus the button element on mount
if (myButtonRef.current) {
myButtonRef.current.focus();
}
}, []);
return (
<div>
<h1>useRef Example</h1>
<button ref={myButtonRef} onClick={handleClick}>
Click me!
</button>
<p>Click count: {countRef.current}</p>
</div>
);
}
export default App;
directly updating the countRef.current value won't trigger a re-render of the component to reflect the updated value in the UI because useRef doesn't trigger re-renders when its value changes. To display the updated count in the UI, you should use useState or useReducer to manage the state.
useReducer
is another React Hook that is used for state management, especially when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. It is similar to useState
, but it offers more control over state updates, making it suitable for managing more complex state transitions.
Here's how you can use useReducer
with a code example:
import React, { useReducer } from 'react';
// Reducer function that takes the current state and an action and returns the new state
const counterReducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
case 'RESET':
return { count: 0 };
default:
return state;
}
};
function Counter() {
// Initialize state using useReducer
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<h1>Counter</h1>
<p>Count: {state.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;
In this example:
-
We define a reducer function
counterReducer
that takes the current state and an action object as parameters and returns the new state based on the action type. -
Inside the
Counter
component, we use theuseReducer
hook to initialize the state. We pass thecounterReducer
function as the first argument and an initial state object ({ count: 0 }
) as the second argument. -
In the JSX, we display the current count from the state using
state.count
. We also have three buttons that dispatch different actions when clicked: "Increment," "Decrement," and "Reset." Each button callsdispatch
with an action object containing atype
property, which is used by the reducer to determine how to update the state.
When a button is clicked, the counterReducer
function is called with the current state and the action, and it returns the new state. React then re-renders the component with the updated state, reflecting the changes in the UI.
useReducer
is particularly useful when you have more complex state updates or when the state logic depends on the previous state. It allows you to centralize state transitions and makes it easier to test and reason about state changes in your application.
useCallback
is a React Hook that's used to memoize functions, especially when passing them as props to child components or when they are dependencies for other hooks. It helps optimize performance by preventing unnecessary re-creations of function instances.
While useCallback
is used to memoize functions to prevent unnecessary re-creations, if you also want to optimize the rendering of child components, you can use React.memo
in conjunction with useCallback
. React.memo
is a higher-order component (HOC) that memoizes a functional component, preventing it from re-rendering unless its props change.
Here's an updated example with both useCallback
and React.memo
for better optimization:
ParentComponent.js:
import React, { useState, useCallback } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<h1>Parent Component</h1>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
<ChildComponent onClick={handleClick} />
</div>
);
}
export default ParentComponent;
ChildComponent.js:
import React from 'react';
const ChildComponent = React.memo(({ onClick }) => {
return (
<div>
<h2>Child Component</h2>
<button onClick={onClick}>Increment from Child</button>
</div>
);
});
export default ChildComponent;
In this updated example:
-
ChildComponent
is wrapped withReact.memo
. This memoizes the component so that it won't re-render unless its props change. This is especially useful when you want to optimize the rendering of child components. -
The
onClick
prop is passed toChildComponent
, and when the "Increment from Child" button is clicked, it calls theonClick
prop, which is the memoizedhandleClick
function from the parent component.
By using both useCallback
in the parent component and React.memo
in the child component, you optimize performance by avoiding unnecessary re-renders of the child component when its props haven't changed. This combination is particularly effective in scenarios where you have a large number of child components or complex rendering logic.
useCallback
is a React Hook that memoizes functions, and it plays a crucial role in preventing unnecessary re-creations of function instances, especially when used in conjunction with child components. To understand how useCallback
accomplishes this, let's break down the process step by step:
-
The Problem of Function Re-Creation:
In a typical React component, functions declared inside the component are created anew on every render. Consider this example:
import React, { useState } from 'react'; function ParentComponent() { const [count, setCount] = useState(0); // This function is re-created on every render const handleClick = () => { setCount(count + 1); }; return ( <div> <h1>Parent Component</h1> <p>Count: {count}</p> <button onClick={handleClick}>Increment</button> </div> ); }
In this example, the
handleClick
function is re-created every timeParentComponent
renders, even ifcount
hasn't changed. This is because JavaScript creates a new function instance each time the component is re-rendered. -
The Role of
useCallback
:useCallback
is used to memoize functions, which means it remembers the function instance between renders unless its dependencies change. It takes two arguments:- The first argument is the function you want to memoize.
- The second argument is an array of dependencies. If any of these dependencies change between renders, the function is re-created; otherwise, it's reused.
Here's how
useCallback
can be used to prevent unnecessary function re-creation:import React, { useState, useCallback } from 'react'; function ParentComponent() { const [count, setCount] = useState(0); // Memoize the function using useCallback const handleClick = useCallback(() => { setCount(count + 1); }, [count]); // The dependency array includes 'count' return ( <div> <h1>Parent Component</h1> <p>Count: {count}</p> <button onClick={handleClick}>Increment</button> </div> ); }
In this updated example,
handleClick
is wrapped inuseCallback
, and the dependency array[count]
is provided. Now,handleClick
will only be recreated ifcount
changes. Ifcount
remains the same between renders, the same function instance is reused. -
Child Components and
useCallback
:When you pass a memoized function like
handleClick
to child components, you're ensuring that the child components receive the same function instance across renders, as long as the dependencies haven't changed.This is important because if you didn't use
useCallback
, every time the parent component re-renders, it would create a newhandleClick
function, and if you passed this new function to child components, it would cause those child components to re-render unnecessarily, even if the function logic hasn't changed.By using
useCallback
, you optimize the performance of your application by preventing these unnecessary re-renders in child components. The child components don't re-render unless the actual dependencies (in this case,count
) change, allowing for more efficient updates.
useMemo
is a React Hook that is used to memoize the result of a computation. Memoization is an optimization technique where you store the result of a function call and return the cached result when the same inputs occur again. This can help prevent unnecessary re-computation of values, especially when the computation is expensive or when you want to optimize the performance of your React components.
The useMemo
hook takes two arguments:
- A function that computes the memoized value.
- An array of dependencies.
The function is executed during the rendering phase, and its result is cached. The cached result is returned whenever the component re-renders as long as none of the dependencies in the dependency array have changed. If any of the dependencies have changed since the last render, the function is re-executed, and the new result is cached.
Here's the basic syntax of useMemo
:
const memoizedValue = useMemo(() => {
// computation or value to be memoized
}, [dependency1, dependency2, ...]);
-
memoizedValue
: The computed and memoized value. -
() => {...}
: The function that computes the memoized value. -
[dependency1, dependency2, ...]
: An array of dependencies that trigger the recomputation of the memoized value if any of these dependencies change.
example of useMemo
where we calculate the square of a number and memoize the result:
import React, { useState, useMemo } from 'react';
function App() {
const [number, setNumber] = useState(1);
// Calculate the square of the number using useMemo
const squaredNumber = useMemo(() => {
console.log(`Calculating square of ${number}...`); // Log when the computation happens
return number * number;
}, [number]); // The dependency array specifies when the value should be recomputed
const handleNumberChange = (e) => {
const newNumber = parseInt(e.target.value, 10);
setNumber(newNumber);
};
return (
<div>
<h1>useMemo Example</h1>
<label>
Enter a number:
<input type="number" value={number} onChange={handleNumberChange} />
</label>
<p>Square of {number} is: {squaredNumber}</p>
</div>
);
}
export default App;