Question 316
5. **Concurrency and Prioritization**
- React can prepare multiple versions of UI at once (e.g., during slow data loading).
- Updates can be assigned priorities, so urgent updates (like clicks) are handled faster than background work.
Question 316
Question 317
The useId hook is a React hook introduced in React 18 that generates unique IDs that are stable across server and client renders. It's primarily used for accessibility attributes like linking form labels to inputs.
const id = useId();
import { useId } from 'react';
function EmailField() {
const id = useId();
return (
<label htmlFor={id}>Email:</label>
<input id={id} type="email" />
);
}
htmlFor, aria-describedby, aria-labelledby)Note: The IDs generated by useId contain colons (:) which may not work in CSS selectors. For multiple related IDs, you can use the same id as a prefix: ${id}-firstName, ${id}-lastName.
Question 318
The useDeferredValue hook is used to defer updating a part of the UI to keep other parts responsive. It accepts a value and returns a "deferred" version of that value that may lag behind. This is useful for optimizing performance when rendering expensive components.
const deferredValue = useDeferredValue(value);
import { useState, useDeferredValue, useMemo } from 'react';
function SearchResults({ query }) {
// Expensive computation or large list filtering
const results = useMemo(() => {
return largeDataSet.filter(item =>
item.name.toLowerCase().includes(query.toLowerCase())
);
}, [query]);
return (
<ul>
{results.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
);
}
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
<SearchResults query={deferredQuery} />
);
}
The input stays responsive while the expensive SearchResults component re-renders with a slight delay using the deferred value.
Question 319
The useTransition hook allows you to mark certain state updates as non-urgent transitions, keeping the UI responsive during expensive re-renders. It returns a isPending flag and a startTransition function.
const [isPending, startTransition] = useTransition();
import { useState, useTransition } from 'react';
function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('home');
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
return (
<button onClick={() => selectTab('home')}>Home</button>
<button onClick={() => selectTab('posts')}>Posts (slow)</button>
<button onClick={() => selectTab('contact')}>Contact</button>
{isPending && <Spinner />}
{tab === 'home' && <HomeTab />}
{tab === 'posts' && <PostsTab />} {/* Expensive component */}
{tab === 'contact' && <ContactTab />}
);
}
| Feature | useTransition | useDeferredValue |
|---|---|---|
| Controls | State updates (wraps setState) | Values (wraps a value) |
| Use case | When you control the state update | When you receive a value from props or other hooks |
| Returns | [isPending, startTransition] | Deferred value |
| Pending state | Built-in isPending flag | Manual comparison needed |
Question 320
The useSyncExternalStore hook is designed to subscribe to external stores (non-React state sources) in a way that's compatible with concurrent rendering. It's primarily used by library authors for state management libraries.
const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?);
import { useSyncExternalStore } from 'react';
function getSnapshot() {
return navigator.onLine;
}
function subscribe(callback) {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
}
function useOnlineStatus() {
return useSyncExternalStore(subscribe, getSnapshot, () => true);
}
function StatusBar() {
const isOnline = useOnlineStatus();
return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}
This hook ensures that when the external store changes, React re-renders consistently without tearing (showing inconsistent data).
Question 321
The useInsertionEffect hook is designed for CSS-in-JS library authors to inject styles into the DOM before any layout effects run. It fires synchronously before DOM mutations.
useInsertionEffect(() => {
// Insert styles here
return () => {
// Cleanup
};
}, [dependencies]);
1. useInsertionEffect → Inject styles
2. DOM mutations → React updates DOM
3. useLayoutEffect → Read layout, synchronously re-render if needed
4. Browser paint → User sees the result
5. useEffect → Side effects run
import { useInsertionEffect } from 'react';
let isInserted = new Set();
function useCSS(rule) {
useInsertionEffect(() => {
if (!isInserted.has(rule)) {
isInserted.add(rule);
const style = document.createElement('style');
style.textContent = rule;
document.head.appendChild(style);
}
}, [rule]);
}
function Button() {
useCSS('.dynamic-btn { background: blue; color: white; }');
return <button className="dynamic-btn">Click me</button>;
}
Note: This hook is not intended for application code. It's specifically for CSS-in-JS libraries like styled-components or Emotion to prevent style flickering.
Question 322
Custom hooks allow you to extract and share stateful logic between components without changing their hierarchy. The state itself is not shared—each component using the hook gets its own isolated state.
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// Get stored value or use initial value
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
// Update localStorage when state changes
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(storedValue));
} catch (error) {
console.error(error);
}
}, [key, storedValue]);
return [storedValue, setStoredValue];
}
// Usage in multiple components
function ThemeToggle() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Current: {theme}
</button>
);
}
function FontSizeSelector() {
const [fontSize, setFontSize] = useLocalStorage('fontSize', 16);
return (
<input
type="range"
value={fontSize}
onChange={(e) => setFontSize(Number(e.target.value))}
/>
);
}
Both components use useLocalStorage, but each has its own independent state that persists to localStorage.
Question 323
The useDebugValue hook is used to display a label for custom hooks in React DevTools. It helps developers debug custom hooks by showing meaningful information.
useDebugValue(value);
useDebugValue(value, formatFn); // With optional formatter
import { useState, useEffect, useDebugValue } from 'react';
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
// Shows "OnlineStatus: Online" or "OnlineStatus: Offline" in DevTools
useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
}
function useUser(userId) {
const [user, setUser] = useState(null);
// The format function only runs when DevTools is open
useDebugValue(user, (user) => user ? `User: ${user.name}` : 'Loading...');
return user;
}
Note: Only use useDebugValue in custom hooks that are part of shared libraries. It's not necessary for every custom hook in application code.
Question 324
The cleanup function in useEffect is used to clean up side effects before the component unmounts or before the effect runs again. This prevents memory leaks, stale data, and unexpected behavior.
useEffect(() => {
// Setup code
return () => {
// Cleanup code
};
}, [dependencies]);
1. Event Listeners
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
2. Timers and Intervals
useEffect(() => {
const intervalId = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(intervalId);
}, []);
3. Subscriptions
useEffect(() => {
const subscription = dataSource.subscribe(handleChange);
return () => subscription.unsubscribe();
}, [dataSource]);
4. Abort Fetch Requests
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then(response => response.json())
.then(data => setData(data))
.catch(err => {
if (err.name !== 'AbortError') {
setError(err);
}
});
return () => controller.abort();
}, [url]);
When Cleanup Runs:
Question 325
useEvent is an experimental hook (not yet stable in React) designed to solve the problem of creating stable event handlers that always access the latest props and state without causing re-renders or needing to be in dependency arrays.
// Problem: onTick changes on every render, causing interval to reset
function Timer({ onTick }) {
useEffect(() => {
const id = setInterval(() => {
onTick(); // Uses stale closure if onTick is not in deps
}, 1000);
return () => clearInterval(id);
}, [onTick]); // Adding onTick causes interval to reset frequently
}
import { useEvent } from 'react'; // Experimental
function Timer({ onTick }) {
const stableOnTick = useEvent(onTick);
useEffect(() => {
const id = setInterval(() => {
stableOnTick(); // Always calls latest onTick
}, 1000);
return () => clearInterval(id);
}, []); // No dependency needed!
}
| Feature | useEffect | useEvent (experimental) |
|---|---|---|
| Purpose | Run side effects | Create stable callbacks |
| Runs | After render | During render (creates function) |
| Returns | Cleanup function | Stable event handler |
| Closure | Captures values at render time | Always accesses latest values |
| Dependencies | Must list all used values | Not needed in other hooks' deps |
Note: Until useEvent is stable, you can use useCallback with useRef as a workaround for stable callbacks.
Question 326
Following best practices ensures your hooks are predictable, maintainable, and bug-free.
npm install eslint-plugin-react-hooks --save-dev
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
// ❌ Bad: One hook doing too much
function useEverything() {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
const [theme, setTheme] = useState('light');
// ... lots of unrelated logic
}
// ✅ Good: Separate concerns
function useUser() { /* user logic */ }
function usePosts() { /* posts logic */ }
function useTheme() { /* theme logic */ }
// ❌ Bad
function useData() { }
// ✅ Good
function useUserAuthentication() { }
function useFetchProducts() { }
function useFormValidation() { }
// ❌ Bad: Missing dependency
useEffect(() => {
fetchUser(userId);
}, []); // userId is missing
// ✅ Good: All dependencies listed
useEffect(() => {
fetchUser(userId);
}, [userId]);
// ❌ Bad: New object on every render
useEffect(() => {
doSomething(options);
}, [{ page: 1, limit: 10 }]); // Always different reference
// ✅ Good: Memoize or extract
const options = useMemo(() => ({ page: 1, limit: 10 }), []);
useEffect(() => {
doSomething(options);
}, [options]);
Always return a cleanup function when subscribing to events, timers, or external data sources.
Question 327
If you try to update the state directly then it won't re-render the component.
//Wrong
this.state.message = "Hello world";
Instead use setState() method. It schedules an update to a component's state object. When state changes, the component responds by re-rendering.
//Correct
this.setState({ message: "Hello World" });
Note: You can directly assign to the state object either in constructor or using latest javascript's class field declaration syntax.
Question 328
The callback function provided as the second argument to setState is executed after the state has been updated and the component has re-rendered. Because setState() is asynchronous, you cannot reliably perform actions that require the updated state immediately after calling setState. The callback ensures your code runs only after the update and re-render are complete.
this.setState({ name: "Sudheer" }, () => {
console.log("The name has been updated and the component has re-rendered.");
});
Use the setState callback when you need to perform an action immediately after the DOM has been updated in response to a state change. i.e, The callback is a reliable way to perform actions after a state update and re-render, especially when the timing is critical due to the asynchronous nature of state updates in React. For example, if you need to interact with the updated DOM, trigger analytics, or perform further computations that depend on the new state or rendered output.
useEffect hook to respond to state changes.componentDidUpdate for broader post-update logic.setState callback is still useful for one-off actions that directly follow a specific state change.Question 329
There are 3 possible ways to achieve this in class components:
class User extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log("SingOut triggered");
}
render() {
return <button onClick={this.handleClick}>SingOut</button>;
}
}
handleClick = () => {
console.log("SingOut triggered", this);
};
<button onClick={this.handleClick}>SingOut</button>
handleClick() {
console.log('SingOut triggered');
}
render() {
return <button onClick={() => this.handleClick()}>SignOut</button>;
}
Note: If the callback is passed as prop to child components, those components might do an extra re-rendering. In those cases, it is preferred to go with .bind() or public class fields syntax approach considering performance.
Question 330
You can use an arrow function to wrap around an event handler and pass parameters:
<button onClick={() => this.handleClick(id)} />
This is an equivalent to calling .bind:
<button onClick={this.handleClick.bind(this, id)} />
Apart from these two approaches, you can also pass arguments to a function which is defined as arrow function
<button onClick={this.handleClick(id)} />;
handleClick = (id) => () => {
console.log("Hello, your ticket number is", id);
};