Use Prop Getters with Render Props

Share this video with your friends

Send Tweet

When you're using prop collections, sometimes you can run into trouble with exposing implementation details of your prop collections. In this lesson, we'll see how we can abstract that away by simply creating a function called a prop getter that will compose the user's props with our prop collection.

Wix
Wix
~ 6 years ago

I think that when composing event handlers, it is useful to support the event.preventDefault() mechanism. So in callAll() method you could check event.defaultPrevented before calling each function, and stop of default is prevented.

This let's you expose an API similar to the native one. Mostly makes sense when you are implementing a simple component which wraps a single native element.

Its true that in this example you could prevent the togller's onClick from being called, by supplying the onClick after the spread of {...togglerProps}, but if some consumer of this component is passing onClick to a component which uses the Toggler, then that would not be possible.

Erez erezm@wix.com

Kent C. Dodds
Kent C. Dodds(instructor)
~ 6 years ago

Hi Erez, That's a good point! I actually had that functionality in downshift since the initial release. It is awesome! The problem is that if you want to prevent downshift's behavior but not the browser's default behavior you run into issues.

I don't demonstrate how to accomplish this behavior in the course because it's not super common, but maybe I'll add it to the course eventually. By the way, where we landed in 2.0.0 is setting event.nativeEvent.preventDownshiftDefault = true due to issues with React and warnings and things. That seems to work pretty well.

escapiststupor
escapiststupor
~ 6 years ago

How do you avoid unwanted props to be spread across components that doesn't need them? For example, <Switch /> doesn't need this prop ('aria-pressed') to function. To explicitly set 'aria-pressed' to null requires knowledge of the implementation detail of <Toggle /> so it is still taking the abstraction way.

Furthermore, inside the return object of getTogglerProps, we need to know in advance what prop is going to be defined both within <Toggle /> and the user's custom usage (and also to extract that props in the input arguments). For example, if the one who wrote the Toggle component wasn't aware that someone in the future is going to use the onClick prop on the button then how can s/he know in advance that onClick should be extracted and treat specially inside getTogglerProps?

So this pattern seeks to solve a problem that we end up not solving. Am I seeing it wrong?

Phyllis

Kent C. Dodds
Kent C. Dodds(instructor)
~ 6 years ago

The idea of prop getters is that you don't know/care what props are necessary for an element to assume the role of a toggler (in this case) or an autocomplete input (like in the case of downshift's getInputProps). If you notice there are props that you don't want applied for some reason then you can either explicitly opt-out via an override as you suggested, or perhaps what you're rendering isn't the typical toggler and you shouldn't use the prop getter. There's nothing stopping you from not using the prop getters and applying your own props as all the helpers are still available for you to toggle the state.

So this pattern seeks to solve a problem that we end up not solving

If you look at everyone using downshift's prop-getters then you'll see that it definitely solves a problem. The alternative to prop-getters is everyone knowing that they need to supply their input with all of these props. We're definitely solving a problem here 😉

Platon
Platon
~ 6 years ago

Massive thanks for teaching us about getProps. I'm in the process of adopting my old UI toolbox to use it and not looking back - the simplicity and flexibility gain (and therefore reuse potential) is massive compared to the mix and match of other patterns I've been using in the past.

A quick question - is there any reason ever to use compound component pattern in favor of getProps? I can only think of cases when you want to make a component very easy to just "plug and play" out of the box and don't care about granting the user flexibility much if at all (I used MaterialUI and React Select in the past and they had the same interface), and forcing the user to learn your API (and who has time for that?) if he wants to dig deeper. However, it's just as easy to set up default render functions for cases when user has no time to mess with setting up the render props chain.

I tried to make compound components work for a long time and in many different combinations and they just seem so clumsy especially when mixed with other patterns.

re: what Wix is saying, instead of callAll I just use the following onClick inside the object returned from getFooProps:

onClick(e, ...args) => {
    e.stopPropagation // and /or e.preventDefault etc.
    anyOtherFunction(anyOtherArguments) // can be e, args, or anything else - e.g. when rendering a user's custom list this could be a list value restructured from getFooProps
    onClick && onClick(e, ...args)
}
Kent C. Dodds
Kent C. Dodds(instructor)
~ 6 years ago

is there any reason ever to use compound component pattern in favor of getProps

Each of these patterns caters to different use cases/preferences. Render props is the most primitive of the render flexibility patterns and you can build other component APIs out of that pattern, so I'd generally build the render props pattern and then build compound components on top of it for a slightly different API that some people seem to prefer. Here's a real world example of doing that with downshift: https://codesandbox.io/s/github/kentcdodds/downshift-examples/tree/master/?module=%2Fsrc%2Fother-examples%2Fhoc%2Findex.js&moduleview=1

Good luck!

Kent C. Dodds
Kent C. Dodds(instructor)
~ 6 years ago

Oh, and to address onClick && onClick(e, ...args), that's fine, but if you have several of these, it's nice to have the callAll abstraction. See https://github.com/paypal/downshift/blob/master/src/downshift.js

Nazariy
Nazariy
~ 5 years ago

@ 02:51. If onClick is not defined we will get the cannot destructure onClick from undefined error. So I fail to see what the onClick && onClick(...args) is guarding against. Unless, of course, we pass the undefined onClick.