useDebugValue()
| Since: | React 16.8(2019) |
|---|
useDebugValue in React is a hook for displaying a label on a custom hook in React DevTools. It makes it easier to inspect the internal state of a custom hook during debugging.
Syntax
import { useDebugValue } from 'react';
// Basic syntax: displays the value as-is as the label
useDebugValue(value);
// With a format function: defers evaluation of expensive formatting
useDebugValue(value, function(val) {
return formatLabel(val);
});
Parameters
| Parameter | Description |
|---|---|
| value | The value to display in React DevTools. Any type is accepted. |
| format (optional) | A format function called only when DevTools inspects the hook. Receives value and returns the display value. If omitted, value is displayed as-is. |
Sample Code
Basic: display a boolean value as-is
The simplest usage. Passing a value directly displays it in DevTools as useOnlineStatus: true.
import { useState, useEffect, useDebugValue } from 'react';
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(function() {
function handleOnline() { setIsOnline(true); }
function handleOffline() { setIsOnline(false); }
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return function() {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
// DevTools displays "useOnlineStatus: true" or "useOnlineStatus: false"
useDebugValue(isOnline);
return isOnline;
}
function StatusBadge() {
const isOnline = useOnlineStatus();
return <span>{isOnline ? 'Connected' : 'Disconnected'}</span>;
}
export default StatusBadge;
Using a format function for a readable label
Passing a format function as the second argument means it is only called when DevTools actually inspects the hook. This is useful for converting booleans or Date objects into human-readable strings.
import { useState, useEffect, useDebugValue } from 'react';
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(function() {
function handleOnline() { setIsOnline(true); }
function handleOffline() { setIsOnline(false); }
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return function() {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
// DevTools displays "useOnlineStatus: Online" or "useOnlineStatus: Offline"
useDebugValue(isOnline, function(online) {
return online ? 'Online' : 'Offline';
});
return isOnline;
}
function StatusBadge() {
const isOnline = useOnlineStatus();
return <span>{isOnline ? 'Connected' : 'Disconnected'}</span>;
}
export default StatusBadge;
Practical custom hook: showing form validation state
An example that centralizes form validation state for multiple fields in a custom hook and makes it easier to debug in DevTools. This assumes a form for entering training data.
import { useState, useDebugValue } from 'react';
function useFormField(initialValue, validate) {
const [value, setValue] = useState(initialValue);
const [touched, setTouched] = useState(false);
var error = touched ? validate(value) : null;
// DevTools displays "useFormField: Son Goku (OK)" or "useFormField: (Error: ...)"
useDebugValue({ value, error }, function(state) {
if (state.error) {
return state.value + ' (Error: ' + state.error + ')';
}
return state.value + ' (OK)';
});
function handleChange(e) {
setValue(e.target.value);
}
function handleBlur() {
setTouched(true);
}
return { value, error, handleChange, handleBlur };
}
function TrainingForm() {
var nameField = useFormField('', function(v) {
return v.trim() === '' ? 'Name is required' : null;
});
var powerField = useFormField('', function(v) {
return isNaN(Number(v)) ? 'Power level must be a number' : null;
});
return (
<form>
<div>
<label>Name</label>
<input
value={nameField.value}
onChange={nameField.handleChange}
onBlur={nameField.handleBlur}
placeholder="Son Goku"
/>
{nameField.error && <span style={{ color: 'red' }}>{nameField.error}</span>}
</div>
<div>
<label>Power Level</label>
<input
value={powerField.value}
onChange={powerField.handleChange}
onBlur={powerField.handleBlur}
placeholder="9000"
/>
{powerField.error && <span style={{ color: 'red' }}>{powerField.error}</span>}
</div>
</form>
);
}
export default TrainingForm;
Overview
useDebugValue is a hook for displaying the internal state of a custom hook as a label in React DevTools. When inspecting the component tree in DevTools, it lets you customize the information shown next to a custom hook, making debugging more efficient.
The format function passed as the second argument is called only when DevTools actually inspects that hook. When label generation involves date formatting or heavy computation, passing it as a format function avoids unnecessary cost during normal rendering.
useDebugValue can only be used inside custom hooks. Calling it directly inside a component has no meaningful effect. It is intended for debugging support in libraries or large-scale projects that use custom hooks, and has no impact on production behavior.
Related pages: Custom Hook / useState
Common Mistakes
Calling useDebugValue directly inside a component
useDebugValue only has meaning inside a custom hook. Calling it directly inside a component will display a label on that component in DevTools, but this is not the intended usage. It is not designed to be used outside custom hooks.
component_debug_ng.jsx
import { useState, useDebugValue } from 'react';
// useDebugValue is called directly inside a component
// The DevTools display may not behave as intended
function PowerMeter() {
const [power, setPower] = useState(9000);
// Should be called inside a custom hook, not here
useDebugValue(power, function(v) { return 'Power: ' + v; });
return <p>Current power: {power}</p>;
}
After fix: call it inside a custom hook.
component_debug_ok.jsx
import { useState, useDebugValue } from 'react';
// Call useDebugValue inside the custom hook
function usePowerLevel(initial) {
const [power, setPower] = useState(initial);
useDebugValue(power, function(v) { return 'Power: ' + v; });
return [power, setPower];
}
function PowerMeter() {
const [power, setPower] = usePowerLevel(9000);
return (
<div>
<p>Current power: {power}</p>
<button onClick={function() { setPower(function(p) { return p * 10; }); }}>
Power Up
</button>
</div>
);
}
Writing heavy computation directly in the label without a format function
Writing expensive computation directly as the first argument to useDebugValue means that computation runs on every render, whether DevTools is inspecting or not. Using a format function ensures the computation only runs when DevTools inspects the hook, avoiding impact on normal rendering performance.
heavy_label_ng.jsx
import { useState, useDebugValue } from 'react';
function useRosterStats(members) {
const [roster, setRoster] = useState(members);
// This computation runs on every render
var label = roster.map(function(m) { return m.name; }).join(', ')
+ ' (Total power: ' + roster.reduce(function(sum, m) { return sum + m.power; }, 0) + ')';
useDebugValue(label);
return [roster, setRoster];
}
After fix: pass the computation to the format function for deferred evaluation.
heavy_label_ok.jsx
import { useState, useDebugValue } from 'react';
function useRosterStats(members) {
const [roster, setRoster] = useState(members);
// The format function is called only when DevTools inspects the hook
useDebugValue(roster, function(r) {
return r.map(function(m) { return m.name; }).join(', ')
+ ' (Total power: ' + r.reduce(function(sum, m) { return sum + m.power; }, 0) + ')';
});
return [roster, setRoster];
}
function DragonBallRoster() {
var [roster] = useRosterStats([
{ name: 'Son Goku', power: 9000 },
{ name: 'Vegeta', power: 8500 },
{ name: 'Piccolo', power: 3500 },
]);
return (
<ul>
{roster.map(function(m) {
return <li key={m.name}>{m.name}: {m.power}</li>;
})}
</ul>
);
}
export default DragonBallRoster;
If you find any errors or copyright issues, please contact us.