31
loading...
This website collects cookies to deliver better user experience
useImperativeHandle
, useLayoutEffect
, and useDebugValue
. We’ll explore their use cases, syntax, and a few code examples. Let’s get started!useImperativeHandle
Hook, which allows us to expose a value, state, or function inside a child component to the parent component through ref
. You can also decide which properties the parent component can access, thereby maintaining the private scoping of the child component.useImperativeHandle(ref, createHandle, [dependencies])
ref
: the ref passed down from the parent componentcreateHandle
: the value to be exposed to the parent componentdependencies
: an array of values that causes the Hook to rerun when changeduseImperativeHandle
Hook is a great choice.useImperativeHandle
Hook when I needed to open a modal component when a button in the parent component was clicked.Modal
component using useImperativeHandle
and forwardRef
.import React, { useRef } from 'react';
import Child from './Child';
const ParentComponent = () => {
const childRef = useRef(null);
const handleOpenModal = (value) => {
childRef.current.openModal(value);
}
return (
<div>
<p>This is a parent component</p>
<Child ref={childRef}/>
<button onClick={() => handleOpenModal(true)}>Open modal</button>
</div>
)
}
export default ParentComponent;
ref
, which we passed down to the child component. In our code below, the ref
will be the first parameter passed to useImperativeHandle
in the child component.handleOpenModal
function, which returns the openModal
function passed up from the child component with childRef.current.openModal(value)
. The function is then called when the button is clicked.import React, { forwardRef, useImperativeHandle, useState } from 'react';
function ChildComponent (props, ref) {
const [openModal, setOpenModal] = useState(false);
useImperativeHandle(ref, () => ({
openModal: (value) => setOpenModal(value),
}));
if(!openModal) return null;
return (
<div>
<p>This is a modal!</p>
<button onClick={() => setOpenModal(false)}> Close </button>
</div>
)
}
export default forwardRef(ChildComponent);
forwardRef
to expose the openModal
function defined in useImperativeHandle
. In the parent component, the state defined in the child component is changed, causing a re-render of only the child component. Problem solved!useEffect
Hook, the useLayoutEffect
Hook lets you perform side effects like API calls, setting up subscriptions, and manually manipulating the DOM in a function component.useEffect
and useLayoutEffect
after performing the DOM updates, useLayoutEffect
is called before the browser paints those updates for users to see, synchronously, while useEffect
is called after the browser paints those updates, asynchronously.useLayoutEffect
runs. For this reason, you’ll mostly use useEffect
, which shows your users something like a loader in the browser while the side effects are being run.useLayoutEffect
with the following syntax.useLayoutEffect(callback, [dependencies])
callback
: the function that contains the side effect logicdependencies
: an array of dependencies. The callback function is run again when the value of any of the dependencies changesimport React, {
useState,
useLayoutEffect,
useEffect
} from 'react';
const TestHooks = () => {
const [randomNumber, setRandomNumber] = useState(0);
useEffect(() => {
if (randomNumber === 0) {
setRandomNumber(Math.random() * 1000);
}
}, [randomNumber]);
return (
<div className='App'>
<p>{randomNumber}</p>
<button onClick={() => setRandomNumber(0)}>
Change value
</button>
</div>
);
};
export default TestHooks;
0
. If we run the code above with the useEffect
Hook, you’ll notice a flickering effect as the number changes from 0
to the next random number when the reset button is clicked.useEffect
to useLayoutEffect
and click the button again. The transition to the next random number is smoother.useEffect
:DOM updates => Browser paints the update for users to see => useEffect is run => another DOM update => Broswer paints the second update for user to see
useLayoutEffect
:DOM updates => useLayoutEffect is run =>another DOM update => Broswer paints the overall update for user to see
useEffect
Hook and React Router DOM for routing. However, I noticed that the window scroll position of the page didn’t move to the top when navigating between the different pages, instead, scrolling began from where it was on the previous page, which is not an uncommon occurrence with React Router DOM.useLayoutEffect
as follows:import { useLayoutEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop() {
const { pathname } = useLocation();
useLayoutEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
index.js
file looks like the code below:function Index() {
return (
<Router>
<ScrollToTop />
<App />
</Router>
);
}
useLayoutEffect
, we can make that process seamless.useLayoutEffect
Hook. After all DOM mutations, useLayoutEffect
fires synchronously; therefore, it can be used to read and change the layout in the DOM, from getting the scroll position or other styles for an element to adding animations at a particular scroll position. Be careful though, your user won’t see anything until this Hook is run and another DOM update is made.useDebugValue
improves the developer experience, helping developers log information in React DevTools in an easier format. Note that the useDebugValue
Hook should only be used in combination with a custom React Hook.useDebugValue(value)
useState
or useRef
is used in a custom Hook, it will debug its respective values within React DevTools.import { useDebugValue, useState } from "react"
export default function useCustomHook() {
const [name, setName] = useState("")
const [address, setAddress] = useState("")
return [name, setName, address, setAddress]
}
App.js
and inspect DevTools:import useCustomHook from './useCustomHook';
function App() {
useCustomHook();
return (
<>
<div className="App">
<p>hey</p>
</div>
</>
);
}
export default App;
useState
is already being logged for us. We have two states for name
and address
:name
comes first, so it should be the first state in DevTools.useDebugValue
to display a label for values in our custom Hooks in React DevTools.import { useDebugValue, useState } from "react"
export default function useCustomHook() {
const [name, setName] = useState("")
const [address, setAddress] = useState("")
useDebugValue(name ? 'Name has been set' : 'Name has not been set')
return [name, setName, address, setAddress]
}
name
as its state changes, using useDebugValue
so we don't have to guess what its value is. useDebugValue
is helpful when the Hook’s state is not immediately obvious from looking at the values in DevTools.useDebugValue
takes an optional second parameter, a formatting function. Let's say the value you are receiving needs to be formatted before it is readable, like parsing JSON data or formatting a date. The function takes in the debug value and returns a formatted value, however, it only runs when DevTools are open and the value is being inspected.useDebugValue(date, date => date.toDateString());
useImperativeHandle
Hook allows us to expose a value, state, or function inside a child component to the parent component. TheuseLayoutEffect
lets us perform side effects like API calls, setting up subscriptions, and manually manipulating the DOM in a function component. Lastly, the useDebugValue
Hook makes it easier for developers to log information in React DevTools.