Writing my first custom react hook - useOutsideClick
My first react custom hook in my first blog post

Search for a command to run...
My first react custom hook in my first blog post

Thank you Chris. Glad you liked it
Thanks Rosius
Is this your first blog post? You write really well and you should keep them coming!
Hey Catalin! This means a lot coming from you.
Yes, this is my first post and I plan to continue writing as I learn. I have a blog series in mind where I will build my portfolio in public using next js and host it on vercel
Hello !
"We want to listen to clicks on the whole document as soon as the component mounts or whenever the ref changes. We will do that using the useEffect hook".
You can't listen when a ref changes, it's no like a state. So you can remove "componentRef" from your deps ;)
Hey! Thank you for reading.
Having that ref in the useState dependencies ensure that things work when the element on which the ref is placed, unmounts and mounts.
Imagine a case where clicking on a button shows a sidebar and we want to close the sidebar when the user clicks outside of the sidebar(and not the button). In this case the ref would be undefined as first, but would change later. And we would want to register the onOutsideClick again, passing in the new ref.
Let me know if that doesn't make sense
Hello Rajat ! As I said, you can't listen of a ref inside deps.


I invite you to test this code. When a ref change, the useEffect will not be executed.
Victor
Victor de la Fouchardière You are absolutely right here. Thanks for letting me know.
For others wondering, I created a codepen to test out what Victor told me. You can check that out here: https://codesandbox.io/s/vibrant-heisenberg-5bzrf?file=/src/App.js
Great read. I think you should check out Rooks Library for hooks. It contains a lot of great hooks for all the common cases.
Also, I wanna suggest that you use // for comments inside the code. It's more readable.
Thank you so much for the feedback.
I have updated the code comments to use // instead of #
rooks is amazing. I have been a big fan of both rooks and react-use.
I created this one just as a way to learn how to create custom react hooks
the future is closer than ever

"You can't build a great building on a weak foundation." — Gordon B. Hinckley

Centering content in CSS is something that beginners struggle a lot with. CSS provides a lot of options that allow you to center content inside a container. We'll try some of the most commonly used ones. Centering text Let's start by learning how to ...

When react hooks were launched, they completely changed the react ecosystem. I have been using react hooks for quite some time now and I am a big fan. But like a lot of other developers, I have never written a custom react hook. This is mainly because firstly, all the functionality I need is available in a third-party hooks library, and secondly, procrastination.
I am a firm believer in learning by doing. So I am going to create a very simple hook - useOutsideClick. This hook will help us to trigger a function when a user clicks outside a component.
and many more
This may not be the best way, but I have been using a very simple approach in my older class-based components. I will just try to replicate that with a custom hook. Here's what we will do:
onClickListener to the document when the component mountsoutsideClickHandler when the target of the click lies outside the desired componentYou can find the final code of this tutorial in this github repository and a live working demo here
Let's create a react app and run it using the following commands
npx create-react-app useOutsideClick
npm install # to install all dependencies
npm run start # to run the app
We'll first create the outside click functionality in a simple functional component and then try to extract it into a custom hook
Let's edit src/App.js to look like:
import "./styles.css";
export default function App() {
return (
<div className="App">
<div className="main">Click me</div>
</div>
);
}
and update the styles in ./styles.css to make things slightly less ugly
html, body, #root {
display: grid;
place-items: center;
height: 100%;
width: 100%;
}
.main {
background: lightskyblue;
font-size: 2rem;
width: 20vh;
height: 10vh;
display: grid;
place-items: center;
border-radius: 40px;
}
If you check the browser, you'll see something like this

We'll now try to detect when the user has clicked outside the div that says "click me" using the useEffect and useRef hooks.
We will start by creating a new ref for the <div> outside which we want to detect clicks
const mainRef = useRef();
and pass it as the ref prop to the div
<div className="main" ref={mainRef}>
In our click handler, we will check whether the event.target lies inside the target element. We can do that using the contains function. For now, we will just log if the click is outside the element
const onOutsideClick = (e) => {
const inMain = mainRef.current.contains(e.target);
const isOutside = !inMain;
if (isOutside) {
# call the outside click handler here
console.log("Clicked ouside");
}
};
We want to listen to clicks on the whole document as soon as the component mounts or whenever the ref changes. We will do that using the useEffect hook.
useEffect(() => {
document.addEventListener("click", onOutsideClick);
// cleaning up the event listener when the component unmounts
return () => {
document.removeEventListener("click", onOutsideClick);
};
}, [mainRef]);
Our src/App.js will now be like:
import { useEffect, useRef } from "react";
import "./styles.css";
export default function App() {
const mainRef = useRef();
const onOutsideClick = (e) => {
const inMain = mainRef.current.contains(e.target);
const isOutside = !inMain;
if (isOutside) {
console.log("Clicked ouside");
}
};
useEffect(() => {
document.addEventListener("click", onOutsideClick);
return () => {
console.log("cleanup");
document.removeEventListener("click", onOutsideClick);
};
}, [mainRef]);
return (
<div className="App">
<div className="main" ref={mainRef}>
Click me
</div>
</div>
);
}
That's it. We now just need to extract this functionality in a custom hook.
Create a new file called useOutsideClick.js. We will now copy over the code from our src/App.js file to src/useOutsideClick.js and update it to accept the componentRef and the outsideClickHandler
# src/useOutsideClick.js
import { useEffect } from "react";
export const useOutsideClick = (componentRef, outsideClickHandler) => {
const onOutsideClick = (e) => {
// updated this to use the passed componentRef
if (!componentRef.current) {
return;
}
const inMain = componentRef.current.contains(e.target);
const isOutside = !inMain;
if (isOutside) {
outsideClickHandler();
}
};
useEffect(() => {
document.addEventListener("click", onOutsideClick);
return () => {
console.log("cleanup");
document.removeEventListener("click", onOutsideClick);
};
}, [componentRef]);
};
We will now use this inside our app.
#src/App.js
import { useEffect, useRef } from "react";
import "./styles.css";
import { useOutsideClick } from "./useOutsideClick";
export default function App() {
const mainRef = useRef();
useOutsideClick(mainRef, () => console.log("Clicked outside"));
return (
<div className="App">
<div className="main" ref={mainRef}>
Click me
</div>
</div>
);
}
And things work perfectly 🎉
We will now update our app to showcase one of the use cases. When the user clicks the blue <div>, we will show more content below it. We will hide this content when the user clicks anywhere outside this button on the screen. We maintain this state in the state variable expanded
#src/App.js
import { useEffect, useRef, useState } from "react";
import "./styles.css";
import { useOutsideClick } from "./useOutsideClick";
export default function App() {
const mainRef = useRef();
// initially not expanded
const [expanded, setExpanded] = useState(false);
// set `expanded` to `false` when clicked outside the <div>
useOutsideClick(mainRef, () => setExpanded(false));
return (
<div className="App">
// set `expanded` to `true` when this <div> is clicked
<div className="main" ref={mainRef} onClick={() => setExpanded(true)}>
Click me
</div>
// show more details only when `expanded` is `true`
{expanded && <div className="more">Lorem ipsum dolor sit amet</div>}
</div>
);
}
/* src/styles.css */
/* add this */
.more {
text-align: center;
font-size: 1.2rem;
background: lightskyblue;
}
This is how things look now

Hooray! We have written our first custom hook. You could also check out one of the widely used custom hook libraries( react-use or rooks ) and try to recreate one of the hooks for practice