Inter Caetera

1/31/2020

The story of react-use-message-bar

The best open-source libraries and frameworks are those which solve a real world problem. This was indeed the case with react-use-message-bar. A simple idea from work and a desire for an exercise in developing and testing React hooks were two really good reasons to work on this as a small, Saturday side project.

The Problem

I needed a convenient way to display Office UI Fabric MessageBar components. From that I figured that it would be a really good idea to use, still quite new although definitely familiar now, React hooks to separate the functionality of displaying message bars in different components.

In a related project we used toasts (non-modal notifications that disappeared on a timed basis), but from feedback that we gathered from our users it was clear that if they could, they would rather have message bars or some kind of notification that doesn’t go away after a few seconds. In that project we were too far into the woods to do anything about it (or rather, it would be too much time and effort on something that was just a minor gripe), but in the new one I figured that a proper solution to this would be preferable.

First iteration

My initial idea was to create a hook that returned a container component and a set of actions that would allow for creation and deletion of notifications. Something along the lines of:

    import useMessageBar from 'react-use-message-bar';
    
    const Component = () => {
    	const { MessageBarSlot, createNotification } = useMessageBar();
    
    	const handleClick = () => {
    		createNotification(Date.now());
    	}
    
    	return (
    		<>
    			<MessageBarSlot />
    			<button onClick={handleClick}>Click me!</button>
    		</>
    	);
    }

This solves two problems: first, we do not need to expose to the user an action for deleting a message bar from within itself (you’ll notice that each bar has an “x” button to close it). Second, this is all fairly compact and there really is not that much to process here for the user. The code is simple and quite elegant.

Unfortunately, there are some issues with this approach.

First of all, React hooks are a little bit unwieldy to test. It’s not bad (especially with React Hooks Testing Library), but I would definitely rather avoid testing complicated hooks if I could instead test the components that use those hooks.

Secondly, we do not allow to customize the bar that we render - we would have to ship with a default bar and force the user to use it or add something like a factory that would allow the user to create a hook with an appropriate bar injected into it. That’s actually what we’ll do in the second iteration.

Finally, we strictly tie the slot container and our message bars to the hook. What if the user would like the slot container, but not necessarily the hook? For example, he or she would like to have a top-level slot for important global notifications (such as fetching errors) that is controlled via Redux and local slots that don’t need to use global state and would instead use hooks.

Second iteration

The big thing that changed is that instead of returning a slot component for our message bars, we import it separately. The hook will instead provide props to that slot.

    import { useMessageBar, MessageBarSlotFactory } from 'react-use-message-bar';
    
    const MessageBarSlot = MessageBarSlotFactory();
    
    const Demo = () => {
    	const { createNotification, props } = useMessageBar();
    
    	const handleClick = () => createNotification(Date.now());
    
    	return (
    		<div>
    			<MessageBarSlot {...props} />
    			<button onClick={handleClick}>Create Bar</button>
    		</div>
    	);
    };

Let’s walk through what changed here.

The first thing we see is the MessageBarSlotFactory function which returns a slot component. In the example above it is not passed a parameter, but we could pass it a component as a parameter which would become our new message bar.

Secondly, we return a cryptic props variable out of our hook. The content of those props is not the user’s concern (it contains the array of message bars as well as a function that filters that array). We also return the createNotification action - there is no change here, we had that before.

Let’s see if we managed to solve our problems. The first one is handled well - we can test the slot component separate from the hook using Enzyme or React Testing Library (I chose the latter) and test the hook using React Hooks Testing Library in isolation - after all it’s just returning some data and not components.

We allow the user customization of the message bar that is rendered. The message bar that’s passed to the factory function will always get injected with a handleDismiss function - a function to be called when the user clicks the “x” button or otherwise wants to dismiss the bar - as well as a children object that will be rendered as the content of the bar.

Finally, the user is not tied to the hook if he or she just wants to use the slot for other purposes. This usage is not documented (yet?), but the props object can be created from elsewhere (derived from Redux state, for instance) and substituted in the slot component.

Conclusion

Open source is cool, and I am definitely sure this problem could have been approached in a myriad of different ways. There probably are places to improve and more functionality to add. I am quite happy with this, because, as I said, it was a small Saturday project that turned out very nice.

If you’d like to contribute to react-use-message-bar, you can do that on GitHub.

Divider Divider
Back to top