The Controllable Pattern
Learn how to implement controllable behavior in higher-order components.
We'll cover the following
Components in React are commonly described as controlled or uncontrolled with respect to some variable. If that variable is passed down to it through props
, the component is controlled. If that variable is managed as state
, the component is uncontrolled.
In its original incarnation, Carousel
was uncontrolled with respect to slideIndex
. With the HasIndex
refactoring, the core of Carousel
is now controlled, but the version of Carousel
that this library’s users will consume is still uncontrolled. Nothing outside of Carousel
can modify its slideIndex
because that variable is kept in state.
Suppose you removed the HasIndex
wrapper to make Carousel
controlled. That would make the component more versatile since the user could change the slideIndex
freely. But it would also make it more cumbersome since the user would have to implement their own slideIndexDecrement
and slideIndexIncrement
handlers.
What if you could get the best of both worlds? That’s what the controllable pattern offers. As its name suggests, a controllable component is one that can be optionally controlled. If the user chooses not to control the variable in question, then it functions as an uncontrolled component. The controllable pattern is exemplified by React’s own wrappers around form elements, e.g. <input>
In this section you will modify HasIndex
to make the slideIndex
on Carousel
controllable.
Implementing controllable behaviors
Making Carousel
controllable entails accepting a slideIndex
prop with the following behavior:
- If
slideIndex
is undefined, it continues to function the way it always has, changingslideIndex
internally when the Prev/Next buttons are clicked. - If
slideIndex
is defined, it overrides any internal state.
One implication of this is that the Prev/Next buttons will no longer be able to change the effective slideIndex
if the prop is set. Instead, they should trigger a change event, conventionally named onSlideIndexChange
. This event gives whoever is controlling the Carousel
the option to update the slideIndex
prop.
This is a common source of confusion, so it bears emphasis. When onSlideIndexChange
is called, it does not necessarily mean that the slideIndex
has changed. It can actually mean two things. In the uncontrolled case, it means slideIndex
(the internal value) has changed. In the controlled case, it means slideIndex
would change if the component were uncontrolled, and the controller has the option to change the slideIndex
prop accordingly. A fundamental rule of React is that components have no power to change their own props.
- You can express all of these requirements as tests against
HasIndex
:
Get hands-on with 1400+ tech skills courses.