-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[base] Add prepareForSlot
util
#38138
Conversation
Netlify deploy previewBundle size reportDetails of bundle changes (Toolpad) |
@@ -0,0 +1,21 @@ | |||
import * as React from 'react'; | |||
|
|||
export default function createSlot< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hard part - naming. I'm not sure about "createSlot", as the function does not create a slot.
Something like "prepareForSlot" would be closer to the truth.
Also, please add JSDocs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I have to compare createSlot
and prepareForSlot
, I would go with createSlot
. It's more intuitive and easy to remember (as a user).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it kind of goes against what we try to teach - slots are places where we insert components, so you can't really create them on your own.
@samuelsycamore, what do you think? Maybe something that's not a real word, like "slotify", work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oof this is a hard one. Do we need to keep the basic structure of actionObject
? Here are some spitball ideas to consider:
customizeSlot
insertSlot
insertCustomSlot
wrapCustomSlot
But all of these kind of muddy the definition of a slot, as you point out. 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createSlotComponent
- you are not creating the slot, but the component to be used insideprepareForSlot
- you prepare the component to be used as a slot, maybe shortlyprepareSlot
Two more options. I think if it is up to me I would go withcreateSlotComponent
, it's a bit longer, but descriptive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My gut says to go with createSlotComponent
. I think it offers the most accurate description of what you're doing: creating the component that will go in the slot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forget what I said before! 😅 I think prepareForSlot
is the most accurate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized it's the first name Michal proposed too :D Ok let's go with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've got no remarks on the implementation. From the names you proposed I like prepareForSlot
better, but I'll let @samuelsycamore decide.
Co-authored-by: Sam Sycamore <71297412+samuelsycamore@users.noreply.github.com> Signed-off-by: Marija Najdova <mnajdova@gmail.com>
Updated to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs look good! I think we landed on the best name in the end. 😁
|
||
You can use this object to style your component. | ||
|
||
:::warning | ||
When inserting a component from a third-party library into a slot, you may encounter this warning: `"React does not recognize the ownerState prop on a DOM element."` | ||
This is because the custom component isn't prepared to receive the `ownerState` like a built-in library component would be. | ||
::: | ||
|
||
If you need to use the `ownerState` to propagate some props to a third-party component, you must create a custom wrapper for this purpose. | ||
But if you don't need the `ownerState` and just want to resolve the error, you can use the `prepareForSlot` utility: | ||
|
||
{{"demo": "PrepareForSlot.js", "defaultCodeOpen": true}} | ||
|
||
### Customizing slot props | ||
|
||
Use the `slotProps` prop to customize the inner component props. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off-topic. This page feels a bit out of context in the getting started area. This feels more like a how-to guide or an integration guide.
import * as React from 'react'; | ||
import { prepareForSlot } from '@mui/base/utils'; | ||
import { Button } from '@mui/base/Button'; | ||
import Link from 'next/link'; | ||
|
||
const LinkSlot = prepareForSlot(Link); | ||
|
||
export default function PrepareForSlot() { | ||
return ( | ||
<Button href={'/'} slots={{ root: LinkSlot }}> | ||
Home | ||
</Button> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this example doesn't fully work in real life, for example, this returns a TypeScript error:
<Button href="/" slots={{ root: LinkSlot }} prefetch={false}>
https://nextjs.org/docs/pages/api-reference/components/link#prefetch
Maybe we should remove the prepareForSlot()
API and instead document this?
import * as React from 'react';
import { Button, ButtonProps } from '@mui/base/Button';
import Link, { LinkProps } from 'next/link';
const ButtonLink = React.forwardRef<HTMLAnchorElement, ButtonProps & LinkProps>(
function ButtonLink(props, ref) {
const {
// @ts-expect-error
ownerState,
...other
} = props;
return <Button ref={ref as any} slots={{ root: Link }} {...other} />;
},
);
export default function PrepareForSlot() {
return (
<ButtonLink href="/" prefetch={false}>
Home
</ButtonLink>
);
}
Or maybe this goes back to the need for a component
prop, it's working with Material UI:
For the sake of considering more options, Reshape' button is a span unless a onClick is provided, allowing to compose a > span. it feels quite strange though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We dropped the implicit polymorphism, but it's possible to make it explicit, as described in https://mui.com/base-ui/react-button/#custom-structure
The example should use it:
<Button<typeof LinkSlot> href="/" slots={{ root: LinkSlot }} prefetch={false}>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I didn't know this. This sounds quite interesting.
What happens once a component is customized with styled()
, does it still work?
Could we apply this to our example? As I think the initial point I raise stays true.
Actually, once applied repeatedly, it feels like this asks for a wrapper component to keep it more nominal. Once you introduce a wrapper, you are better not to use prepareForSlot
as it creates fewer React components, better performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens once a component is customized with styled(), does it still work
Nope, this is a limitation of TypeScript. Similarly, in Material UI, you can't use component
prop on a component returned from styled
. It's one of the cases where render props or asChild
work better.
Once you introduce a wrapper, you are better not to use prepareForSlot
That's right. prepareForSlot
is meant to be used with bare 3rd party components. Once you wrap it and can prevent forwarding the ownerState
, you don't have to use the function.
Could we apply this to our example?
Sure: #38640
Related to #32882
The PR adds a
prepareForSlot
util that can be used with third-party libraries' components if they are use inside theslots
prop. Currently if a third-party component is used directly in a slot, there is a React warning shown: "React does not recognize the ownerState prop on a DOM element." In order for this to be fixed, so far developers needed to create wrapper component so that they would intercept theownerState
prop in order to not be propagated to the underlying component. This new util is basically doing this behind the scene.