React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.
In this article, we look at the useLayoutEffect
and useDebugValue
hooks.
useLayoutEffect
useLayoutEffect
has an identical signature to useEffect
, but it fires synchronously after all DOM mutations are done.
This is useful for reading layout from the DOM and synchronously re-render.
Updates inside useLayoutEffect
will be flushed synchronously before the browser has a chance to paint.
To prevent blocking visual updates, we should use useEffect
whenever possible.
useLayoutEffect
fires in the same phase as componentDidMount
and componentDidUpdate
.
We should only use useLayoutEffect
if useEffect
isn’t working properly.
If server rendering is used, the useLayoutEffect
nor useEffect
are run until the JavaScript is downloaded.
React warns when server-rendered component contains useLayourEffect
.
To exclude components that need layout effects from server-rendered HTML, we render it conditionally with showChild && <Child />
and defer showing it with useEffect(() => { setShowChild(true); }, [])
.
Then the UI won’t appear broken before rendering is done.
For example, we can render synchronous with useLayoutEffect
as follows:
function App() {
const [value, setValue] = React.useState(0);
React.useLayoutEffect(() => {
if (value === 0) {
setValue(10 + Math.random() * 200);
}
}, [value]);
return (
<>
<button onClick={() => setValue(0)}>Random Value</button>
<p>{value}</p>
</>
);
}
Since value
is set initially to 0, we want to update that first before rendering, so we use useLayoutEffect
hook and call setValue
in the callback.
Also, since we set value
to 0 when clicking the button, we want to only show the value when value
when it’s not 0, so useLayoutEffect
will prevent 0 from being shown.
useDebugValue
useDebugValue
is used to display a label for custom hooks in React DevTools.
It takes any value that we want to display.
For example, we can use it as follows:
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
const useDelayedMessage = (msg, delay) => {
const [message, setMessage] = useState("");
useEffect(() => {
setTimeout(() => {
setMessage(msg);
}, delay);
});
React.useDebugValue(message ? "Message Set" : "Message not set");
return message;
};
function App() {
const delayedMessage = useDelayedMessage("foo", 1500);
return <div>{delayedMessage}</div>;
}
Since we have:
React.useDebugValue(message ? "Message Set" : "Message not set");
in React DevTools, we’ll see the Message not set
message when the page first loads, and then when message
is set after the delay
, then we see Message set
.
This hook is useful for custom hooks that are part of shared libraries
Defer formatting debug values
In some cases formatting, a value for display may be an expensive operation.
Therefore, useDebugValue
accepts a formatting function as an optional second parameter. The function is only called if the hooks are inspected.
It receives the debug value as a parameter and returns the formatted display value.
For example, we can write:
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
const useDelayedMessage = (msg, delay) => {
const [message, setMessage] = useState("");
useEffect(() => {
setTimeout(() => {
setMessage(msg);
}, delay);
});
React.useDebugValue(message, message =>
message ? "Message Set" : "Message not set"
);
return message;
};
function App() {
const delayedMessage = useDelayedMessage("foo", 1500);
return <div>{delayedMessage}</div>;
}
In the code above, we have:
React.useDebugValue(message, message =>
message ? "Message Set" : "Message not set"
);
which runs the formatting function in the second argument when it’s inspected.
The message
parameter has the same value as message
returned from useState
.
Conclusion
We can use the useLayoutEffect
hook to synchronous run code after all DOM mutations are done.
useDebugValue
hook is handy for debugging with React DevTools since it lets us display something in the DevTools console as the hook runs.