Create your own React hook

Chris Feist
JetClosing Engineering

--

React hooks are a powerful way to supercharge functional components. However, beyond the useState and useEffect hooks, the examples for creating your own hooks are not very extensive. Let’s look at a practical example by implementing a file drag and drop on the whole window, which we’ll call the “Window Drop Zone”.

To get started, let's create our hook file. The naming convention for hooks is “use” plus the hook name, so let's create a file called useWindowDropZone.js with a basic shell function.

When we use the window drop zone hook, we’ll want an onDrop callback that is invoked when the user drops files. We’ll need to use the useEffect hook in order to add and remove the onDrop callback event listener.

The useEffect hook is documented well, but here is a quick explanation: The first argument is a function that is executed on every render. It can optionally return a cleanup function that is executed before every re-render and when the component is unmounted. In our case, we’re adding the drop listener to the window and removing it in the cleanup function. The second argument of useEffect is an optional array of values that the effect depends on. This allows us to conditionally run the effect, instead of executing it on every render. In our case, we are saying only add the drop event listener when the onDrop argument changes. This means we don’t have to unnecessarily remove and re-add the listener on every render.

Let's see our new hook in action:

🤔🤔🤔

It doesn’t appear to be working… What’s happening is that the browser is consuming the event and loading our dropped file. In order to fix this, we need to call the preventDefault method on the event. So we’ll have to supply our own drop event listener and a dragover listener to prevent the default event handling. We could easily create our own event listener functions to handle this. However, since we’re expecting our useWindowDropZone hook to be used in a functional component, that means each render will cause a new function to be created. Thankfully, React already has the useCallback hook that only creates the function once, unless the dependencies change. Let’s see how this looks inside our hook:

So what did we do? We created onDropCallback that prevents the default handling of the event and calls the onDrop argument that is supplied to our hook. Like the useEffect hook, the second argument to the hook is an array of dependencies. In our case, we only want a new onDropCallback to be created when onDrop changes. We also created the onDragoverCallback that prevents the default event handling and we claim it has no dependencies by supplying an empty dependency array. This means that it will only be created once no matter how many times our useWindowDropZone hook is invoked while rendering. We also updated the drop event listener to use our new onDropCallback function, added a new dragover listener, and updated the effects dependencies.

Now let's see if our modified hook works:

Success!

What if we could take this a step further and provide a dragging state from our hook? We can do that by returning an isDragging field from our hook. We’ll have to do some wiring up internally and leverage the useState and useRef hooks:

useRef what!?! An overlooked benefit to this hook is that it allows us to keep track of mutable values while not triggering rerenders. In our case, we need to count how many times we receive the dragenter and dragleave events so we can know when the drag has initially entered and finally left. Since we do want to trigger rendering when the dragging state changes, we use the useState hook. We also added a couple more event listeners and tied it all together. Let’s see our latest changes in action:

It works!

Now that we have a solid functioning hook, we could expand its features to include things such asonDragEnter and onDragLeave callbacks.

The true powers of hooks are yet to be fully realized, but with the right know-how, you can start to harness their power. Hopefully, this post will help you in creating your own hooks or expanding on existing ones. If you have any comments, feel free to leave them below and make sure to follow the JetClosing Engineering blog to see some of the great stuff we’re working on.

--

--

Chris Feist
JetClosing Engineering

Full stack developer specializing in React and React Native front ends, and AWS backends