Requisite React
When was the last time you saw an error and had no idea what it meant (and therefore no idea what to do about it)? Today? Yeah, you're not alone... Let's talk about how to fix that.
React components afford us the opportunity to encapsulate state, logic, and markup in a single place. A component is like a magical machine where you pass in props and a beautiful component pops out the other end.
Unfortunately, truly maintainable, flexible, simple, and reusable components require more thought than: "I need it to do this differently, so I'll accept a new prop for that." Seasoned React developers know this leads to nothing but pain and frustration for both the maintainer and user of the component. Ever hear of the apropcalypse? It's not fun.
The only thing you can really expect about the future is the unexpected. Nobody can know the future for certain, so you need some tools in your toolbelt that help you avoid committing yourself to a path that may not satisfy the needs of the future. With React components, these tools are patterns that have been battle tested in real applications and libraries and if you're not using them in your components today, you're likely dealing with APIs that result in more workarounds than lines of maintainable code.
And that's slowing you down.
One of my favorite tools for creating great component abstractions is the idea of "compound components." The idea of compound components has been around forever (think <select>
and <option>
), and it's been implemented in some of my favorite libraries (check Reach UI). Utilizing compound components, you can go from an inflexible API like this:
<Modal modalTitle="Register" modalLabelText="Registration form" openButtonText="Register" dismissable={true} contents={ <LoginForm onSubmit={register} submitButton={<Button variant="secondary">Register</Button>} /> }/>
To something with ultimate flexibility like this:
<Modal> <ModalOpenButton> <Button variant="secondary">Register</Button> </ModalOpenButton> <ModalContents aria-label="Registration form"> <ModalDismissButton> <CircleButton> <VisuallyHidden>Close</VisuallyHidden> <span aria-hidden>Ć</span> </CircleButton> </ModalDismissButton> <ModalTitle>Register</ModalTitle> <LoginForm onSubmit={register} submitButton={<Button variant="secondary">Register</Button>} /> </ModalContents></Modal>
(Note, the compound components are all the ones that start with Modal
).
I can tell some of you are looking at this thinking: "Huh... well I actually like the first one better." But remember, we can't anticipate all the needs of our component's API in the future, so we want to focus on which will be most adaptable.
Consider the difficulty in the following possible future use cases:
openButtonProps
prop? lol, I wish I could say I've never tried that before š¤¦āāļø)Oh, and the neat thing about this is that if you really do like the first one better, there's nothing stopping you from creating a component that uses the compound components API to expose the same API as the first. But try doing that in the reverse š
Taking things further, if it's really common enough, you could create a special ModalContents
component that handles rendering the dismiss button and title, so in the end it looks like:
<Modal> <ModalOpenButton> <Button variant="secondary">Register</Button> </ModalOpenButton> <ModalContents aria-label="Registration form" title="Register"> <LoginForm onSubmit={register} submitButton={<Button variant="secondary">Register</Button>} /> </ModalContents></Modal>
But you don't have to sacrifice the benefits of the previous API thanks to composition. Sure you could do something similar with our previous API, but have you ever tried to maintain a component with a hodgepodge of props like that? I mean, I love that React components can be black boxes just as much as the next developer, but I'd prefer to not experience soul-crushing maintainability nightmares any time I need to venture into that black box...
The beauty of patterns like compound components, state reducers, prop getters, controlled props, etc. is that you can have your simple API and a simple implementation at the same time. If you aren't doing this yet, now's a great time to start.
And I'm excited to teach you how on EpicReact.Dev.
Delivered straight to your inbox.
When was the last time you saw an error and had no idea what it meant (and therefore no idea what to do about it)? Today? Yeah, you're not alone... Let's talk about how to fix that.
The sneaky, surreptitious bug that React saved us from by using closures.
A basic introduction memoization and how React memoization features work.
Understanding server-side waterfalls with RSCs and client-side waterfalls we're familiar with and why server-side waterfalls are probably better.