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.
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
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.
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
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 😉
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)
}
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!
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
@ 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.