They laid out pretty well what problems this is trying to solve, why they want it that way, the benefits it has, and some of the tradeoffs it makes.
Saying it "replaces composition with configs for no good reason" is not only wrong (they still heavily use composition here, it even makes it more powerful in some ways from what I can tell so far), but also doesn't really mean anything on it's own (at least to me it doesn't).
There’s already a way to overwrite props in react. Just use props.
If you need to expose the native API of the underlying element, do this (does HN format code?)...
``` const {foo, ...native} = props;
<label {...native}> <p>{foo}</p> </label>
```
Combine that with default props and you’re good to go.
There’s already a way to allow components to take multiple types of children.
``` <Select render={ blah => { return <CustomOption>{blah}</CustomOption> }} /> ```
And there’s already a multitude of ways to adapt styles based on props, the most obvious of which is StyledComponents.
In addition, this highlevel config is brittle. It means parents must be coupled to their children, and children coupled to their parents.
And where does this config end? How deep does the nesting of this configuration go?
Here’s my opinion; if you think you need this sort of configuration to properly wrangle your UI, then you’ve failed to grasp the point of React and the true power of composition.
React components are just functions. Preferably pure functions. Once you strip away the JSX, you should be left with something that resembles functional programming (albeit taken to an extreme thanks to JSX’s goal of emulating HTML). Props are parameters. We’ve all come across code where functions take complex and convoluted objects as arguments that trigger an explosion of calls that are near impossible to track or reason about.
This override solution is the first step in towards turning React into the kind of tool that it usurped. It has the stench of ExtJs and it’s ilk.
This “solution” was created by people who believe all problems are better solved by abstraction.
As programmers, our time is spent on the edge cases. Our text book prefect algorithms and mathematically sound functions are exposed as frauds the second they’re exposed to user input. Our carefully crafted UI libraries fall to pieces when the designers choose to break their own rules.
For configuration to work, it requires god level foresight and will eventually become Turing complete in itself.
Config is for the birds. Declarative code has always been the answer.
It actually does everything you propose.
For render props, the BaseWeb API is not <Select renderDropdown={...} renderArrow={...} renderLabel={...} /> but it's collapsed under a single prop overrides={{ Dropdown: { component: {...} } ... }} naming convention. This "map" includes (and gives you access) to every single subcomponent.
The same goes for "adapting styles based on props".
BaseWeb uses exactly same patterns and techniques that you mention. The difference is the naming consistency and uniformity across all components. Instead of "littering" the top level API, we have a single prop "overrides" and instead of deciding what should and shouldn't be customizable, everything is customizable. Again, without a need to keep adding new top level props.
Everything is customisable without this override solution. And I’d argue that things become less customisable with it. What if the CustomOption component needs to be customised too? This solution makes a mess of that type of composition.
Note, that you can replace a single layer and pass the "children" through OR "replace the whole branch" of subcomponents when NOT passing the children through.
Also all overrides get various state props. For dnd-list, it can be something like $isDragged, so you can conditionally render different things.
At the same time, every Base Web component is exporting all these subcomponents. For example, component DND List also exports:
- StyledRoot - StyledList - StyledItem - StyledLabel - StyledCloseHandle - StyledDragHandle
https://baseweb.design/components/dnd-list/#overrides
You are free to use these Styled subcomponents and recompose them any way you wish. You can build your own component from scratch (this approach works great for tables) or you can use overrides and utilize existing state handling etc.
Give me specific example: What customization of dnd-list you can't make with the existing overrides API and what other API would allow you to do that better.
<DNDLlist render={(listItem => {
switch(listItem.type) {
case 'foo':
return <FooListItem>{listItem.title}</FooListItem>;
case 'bar':
return <BarListItem>{listItem.title}</BarListItem>;
default:
return <ListItem>{listItem.title}</ListItem>;
}
})}>Why do I have to specify "List" twice? And what sort of magic is "component" performing? What are children now? Do they not exist anymore? And what does "overrides" mean in this context? Passing children to a component is not an "override".
I think you're drunk on your own hype buddy. Maybe this all makes perfect sense to you, but it looks like garbage to me.
overrides={{ Item: { component:
instead of: renderItem={
(personally I'd probably have a shortcut to allow leaving "{ component:" off though)I don't think that's a bad tradeoff to render props support (plus prop/style overrides) support across your entire component library in a consistent way.
You can use the components as is, using props if you're ok with the out of the box style/functionality/etc, without ever touching overrides. The overrides provide a standardized interface to changing the component internals across the entire component library.
That approach may be overkill for simple components that are trivally composable (Card for example), but is incredibly powerful for a component like Datepicker where you may want to change styles or functionality of a deeply nested component without exposing a massive top level props interface.
If someone needs a component to do something it doesn't do, that's a sign that the primitives are wrong and the component needs to be refactored. Punting the solution back to product teams is prioritising short terms wins at the expense of long term pain.
Imagine if we extended this philosophy to other paradigms: every method and property on a class is public and overridable, just in case we need to change the behaviour.
A concrete example probably does a better job of explaining how this is used: https://medium.com/@dschnr/better-reusable-react-components-...
Referring to the Uber Freight RadioGroup and Tag Edit overrides from that article (not my article to be clear), those are cases where the vast majority of functionality from the original components satisfied the usage requirements, but a small tweak was needed to get the desired outcome. The override pattern is simply a way of building flexibility into the components and acknowledging that the users of the components may want to change them in ways that were not obvious to the component author. Those two examples may be fairly straightforward, but at what point do you stop with exposing the ability to change things? In our prior component library we saw a number of cases where teams wanted the ability to customize things that were more subtle, ie- changing the html element type on a subcomponent for a very specific purpose. This pattern came from observing the usage of our prior internal component library and talking with many of the internal consumers. It's probably not be a fit for everyone, but it works well for us.
Your components now need to follow some pre established and short sighted rules in order to understand how to manage their own children.
To use the example in the article, lets say you have an Option component, but now you want a version of that component that takes two additional components under certain conditions. E.g, an icon, and a checkbox? But only when one specific Option has a specific combination of props?
How does this override system handle that? It doesn’t. Composition handles that. But if components are created with this override system in mind, they lose the ability to be composed in more complex ways than the designer of the override system imagined.
It appears you can pass functions to the config that render components, which presumably can also take an override config which in turn can take a function to render components and so on. So it's basically negating the entire point of JSX. It's halfway between JSX and React.createElement. The worst of both worlds.