December 4, 2024

Boosting Code Quality at Mixmax with Custom React Hooks

Boosting Code Quality at Mixmax with Custom React Hooks

At Mixmax, we’re always looking for ways to improve code quality and streamline our development process. One of our favorite tools in the React ecosystem is custom hooks. By encapsulating logic into reusable hooks, we’ve made our components cleaner, our codebase more maintainable, and our team’s life easier.

Here’s a glimpse into how we leverage custom hooks to solve common challenges like handling hover states, detecting outside clicks, and optimizing initialization.

Simplifying Hover Interactions with useHoverState

Managing hover logic can get repetitive—especially when you need precise control over delays. Our useHoverState hook abstracts this functionality, ensuring consistent behavior while reducing boilerplate.

Hook Implementation

Here’s the useHoverState hook written in JavaScript:

export function useHoverState({
enterDelay = 0,
leaveDelay = 0,
onMouseOver,
onMouseOut,
}) {
const [isHovered, setIsHovered] = useState(false);
const delayTimeoutRef = useRef();

function handleMouseOver(e) {
clearTimeout(delayTimeoutRef.current);

delayTimeoutRef.current = setTimeout(() => {
onMouseOver?.(e);
setIsHovered(true);
}, enterDelay);
}

function handleMouseOut(e) {
clearTimeout(delayTimeoutRef.current);

delayTimeoutRef.current = setTimeout(() => {
onMouseOut?.(e);
setIsHovered(false);
}, leaveDelay);
}

return [
isHovered,
{
onMouseOver: handleMouseOver,
onMouseOut: handleMouseOut,
},
];
}

Basic Example of Usage

Here’s a simple example of how this hook could be used to implement a tooltip:

function Tooltip() {
const [isHovered, hoverEvents] = useHoverState({ enterDelay: 200, leaveDelay: 200 });

return (
<div {...hoverEvents} className="relative">
Hover over me
{isHovered && <div className="tooltip">I'm a tooltip!</div>}
</div>
);
};

This example demonstrates how the hook makes hover-related logic cleaner and easier to manage.

Detecting Outside Clicks with useOnClickOutside

Handling clicks outside an element—such as closing dropdowns or modals—is a common challenge in UI development. Our useOnClickOutside hook encapsulates this behavior, reducing duplication and ensuring consistent implementation.

Hook Implementation

Here’s the useOnClickOutside hook:

export function useOnClickOutside(ref, handler, options) {
useEffect(() => {
const listener = (event) => {
const el = ref.current;
if (!el || el.contains(event.target)) {
return;
}
handler(event);
};

document.addEventListener('mousedown', listener, options);
document.addEventListener('touchstart', listener, options);

return () => {
document.removeEventListener('mousedown', listener, options);
document.removeEventListener('touchstart', listener, options);
};
}, [ref, handler, options]);
}

Basic Example of Usage

Here’s an example of how this hook can be used in a dropdown component:

function Dropdown() {
const ref = useRef(null);
const [isOpen, setIsOpen] = useState(false);

useOnClickOutside(ref, () => setIsOpen(false));

return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle dropdown</button>
{isOpen && (
<div ref={ref} className="dropdown">Dropdown Content</div>
)}
</div>
);
}

This hook makes handling click-outside logic simple, keeping the dropdown component focused on its primary task.

Optimizing Initialization with useLazyRef

React’s useRef initializes values on every render, which can be inefficient for expensive computations. Our useLazyRef hook ensures the initialization happens lazily and only once.

Hook Implementation

Here’s the implementation of useLazyRef:

export function useLazyRef(initCallback) {
const hasBeenInitializedRef = useRef(false);
const ref = useRef(null);

if (!hasBeenInitializedRef.current) {
ref.current = initCallback();
hasBeenInitializedRef.current = true;
}

return ref;
}

Basic Example of Usage

Here’s an example of how useLazyRef can optimize initialization:

function MyComponent() {
const expensiveObjectRef = useLazyRef(() => {
console.log('Initializing expensive object...');
return { data: 'Expensive data' };
});

return <div>Check the console for lazy initialization!</div>;
}

This hook ensures that expensive logic is only executed once, improving performance.

Why We Love Custom Hooks

Custom hooks like these have transformed the way we write and manage React code at Mixmax. They not only reduce boilerplate but also:

  • Encourage consistency: Shared logic is centralized and easier to maintain.
  • Improve readability: Components stay focused on rendering UI, not managing state or events.
  • Enhance scalability: Hooks simplify introducing new features by promoting reusability.
  • Streamline debugging: Centralized logic makes issues easier to trace.

Looking Ahead

Custom hooks have been a game-changer for us at Mixmax, enabling cleaner code and greater reusability. As we continue to scale and enhance our applications, these hooks help us maintain consistency and efficiency across our codebase. Whether you're managing complex interactions or optimizing performance, investing in reusable abstractions like these can make a big difference.

If you're inspired to try out custom hooks or refine your own, we hope these examples offer a solid starting point. Happy coding!

 

Interested in joining the team? Visit Mixmax Careers.

You deserve a spike in replies, meetings booked, and deals won.

Try Mixmax free