Photo by Tamara Gore on Unsplash
When I walk up this morning, I said to myself: “Look David, now is the day, you should try to develop a React custom hooks”.
The experiment went well and was implemented faster than I expected, therefore I thought I could take some time to write about it 😁.
Introduction
Web Components are working everywhere, period. That being said, when used in React, the implementation tends to become a bit more verbose, notably because events
have to be attached “manually”. For example, you would not be able out of the box with a Stencil Web Component to do the following.
<my-component onMyEvent={($event) => console.log($event)}></my-component>
To overcome this issue, you could bundle your Web Component with their related output targets using the stencil-ds-plugins and the problem is solved. But if you don’t, or can’t, then you have to manually register event listeners which, as I said above, could quickly become a bit verbose.
const ref = useRef();
ref.current.addEventListener("myEvent", ($event) => console.log($event));
<my-component ref={ref}></my-component>;
Fortunately, it is possible to create custom hooks and therefore possible to create reusable pieces of code for our application to make it more readable.
Let’s Get Started
For the purpose of this article we are going to start with the very begin, by creating a new React app.
npx create-react-app custom-hook-app
cd custom-hook-app
We want to experiment Web Component, let’s now install one to our application. For example, we can use the color picker of our web open source editor for presentations, DeckDeckGo.
npm install @deckdeckgo/color
Once installed, we can import
and declare it in you application respectively in src/App.js
.
import React, { useEffect, useRef, useState } from "react";
import logo from "./logo.svg";
import "./App.css";
import { defineCustomElements } from "@deckdeckgo/color/dist/loader";
defineCustomElements(window);
function App() {
return (
<div className="App">
<header className="App-header">
<deckgo-color></deckgo-color>
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
If everything went according plan, once we run (npm run start
) our application, the default sample page with our color picker should be rendered.
Implement The Events Listener
Before creating our custom hooks, let’s first implement the events listener as we would do without it. We create a reference useRef
for our component and a state
to render the selected color.
const colorRef = useRef();
const [color, setColor] = useState();
return (
<div className="App">
<header className="App-header">
<deckgo-color ref={colorRef}></deckgo-color>
<img src={logo} className="App-logo" alt="logo" style={{ background: color }} />
</header>
</div>
);
Finally, to attach the events, we use the hooks useEffect
to bind these when our component’s reference is ready.
useEffect(() => {
const ref = colorRef.current;
const colorListener = ($event) => {
// $event.detail.hex is the selected color
setColor($event.detail.hex);
};
// attach the event to the component
ref.addEventListener("colorChange", colorListener, false);
// remove event on component unmount
return () => {
ref.removeEventListener("colorChange", colorListener, true);
};
}, [colorRef]);
I’m agree, not the best UX I ever developed 🤣, but still, we should now be able to select colors and apply them to the background of the React logo.
Create A Custom Hooks
Time to have fun by refactoring our previous implementation in order to create a custom hooks. Firstly, we create a function, the hooks itself, which takes the reference to the component as parameter, contains and return a new state.
function useColorChange(paramColorRef) {
const [data, setData] = useState(undefined);
return [data];
}
To complete our hooks, we move our previous useEffect
code to this new hooks and we adjust the component states to the hooks states. The effect watches the reference passed as parameters and the listener applies the selected color to the hooks state.
function useColorChange(paramColorRef) {
const [data, setData] = useState(undefined);
useEffect(() => {
const ref = paramColorRef.current;
const colorListener = ($event) => {
setData($event.detail.hex);
};
ref.addEventListener("colorChange", colorListener, false);
return () => {
ref.removeEventListener("colorChange", colorListener, true);
};
}, [paramColorRef]);
return [data];
}
Finally, we use our hooks in our application respectively we replace the previous useState
and useEffect.
function App() {
const colorRef = useRef();
const [color] = useColorChange(colorRef);
return (
<div className="App">
<header className="App-header">
<deckgo-color ref={colorRef}></deckgo-color>
<img src={logo} className="App-logo" alt="logo" style={{ background: color }} />
</header>
</div>
);
}
Voilà, isn’t that a cleaner code and pretty cool? And of course, if we redo our test, it should still work out, we should still be able to select a color and apply it to the background of the React logo 😸.
Conclusion
I don’t pretend that the above implementation is the best one, my goal was to try to build a React custom hooks out and to share a comprehensive step by step blog post. I’m pretty sure it could be improved and I would be super duper happy to hear your suggestions about it, ping me with your comments!
To infinity and beyond 🚀
David