تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Styling Links With CVA0:00

In a previous episode, we've seen that we can take the styles from the button components and apply them to a different element like an <a> tag by using the button variance created by CVA. But there's another way to achieve the same using the S child prop. Let's take a look. As a very quick reminder, this is what I'm talking about here. We have an <a> tag and we've imported the button variance.

Introducing asChild Prop0:22

This is what I'm talking about here. We have an anchor tag and we've imported the button variance from the SHA CNUI button component. And so we can style our anchor tag like a destructive button and that's the result. This is an anchor tag as you can see here, but now if we look at the button component from SHA CNUI, you'll see something we briefly glossed over in the previous episode and it is this child property. You can see it sets to false by default and it's a bool.

episode and it is this S child property. You can see it sets to false by default and it's a bool and then it's used here to determine a component which is either a slot if the S child is true, and otherwise if it's false as the default we render a button and then that component is used to render the actual button or the element that could be something else. You can see here we use that comp which comes from here and because by default S child is false

Using asChild in Button1:07

You can see here we use that comp which comes from here and because by default Schild is false by default it'll render a button. But let's try to use that button and use Schild equals true to see what happens. So after the destructive anchor link here, I will use the button, this is the SHA_CNUI one and let's call this one as child. So as is right now, it does not have the Schild prop and it doesn't have any variance.

So as is right now, it does not have the S child prop and it doesn't have any variance. So it should be this black default button and indeed here it is. But now let's try to add the S child properly. We don't need to set it to true, just having it here will set it to true. And now, uh oh, it looks like we are not rendering anything and that's to be expected. The S child naming of the prop sort of hints

and that's to be expected. The child naming of the prop sort of hints that the element should have a child because it's trying to behave as its child. So let's add a child element to our button and see what happens. So inside the button's child before the text, I'm going to have a child element and we're going to recreate this anchor tag with the destructive style.

and we're going to recreate this anchor tag with the destructive style. So let's have an anchor tag and we'll put the child text inside of it and we'll also pass the variant to the button itself. destructive. Aha, here we go. We have our child button and it actually is an anchor tag as well. So essentially these two elements are exactly the same except that this one will inherit all the properties from

So essentially these two elements are exactly the same except that this one will inherit all the properties from the Button component. As you can see here, the data slot equals button, the best way to process in your head. What's happening with the S child prop is whatever element has the S child prop, you can assume that all the functionality from this element will be passed to its immediate child. So here the anchor tag.

to its immediate child. So here the anchor tag. So the S child prop is pretty useful to render accessible and semantic markup like we've done here. Let's say that our link was linking to another page, it would make more sense to have an anchor tag than a button, but you can also make terrible things with it. For example, use a div instead of a button and then the div will have the onclick handler,

Radix Slot Explanation3:08

For example, use a div instead of a button and then the div will have the unclick handler, which is not the end of the world, but it's really not something you should strive to do. Alright, let's take a look at how the S child prop is implemented in chat CN UI and then we are going to recreate a minimal version from scratch so you can understand how it works. Okay, so I guess the S child prop is actually just a boolean, false or true,

Okay, so I guess the slot prop is actually just a boolean, false or true, but what matters is the implementation here where we create a component that if it has a child, uses this slot and otherwise renders the default element, which here is a button. Now if I scroll to the top of the page, you will see that slot is an import from Redix UI React slot. And if you go look at the Redix UI documentation.

that slot is an import from Redx UI react slot. And if you go look at the Redx UI documentation for the slot, it tells us that it merges its prop onto its immediate child and can be used to support your own S child prop, which is exactly what's happening here. And if I scroll down, you'll see some examples and look at this. This is basically exactly what Chat CN UI is doing. Alright, let's go and build our own very basic version

Building a Minimal Slot4:05

This is basically exactly what chat CN UI is doing. Alright, let's go and build our own very basic version of the slot utility. And I say very basic because the redx UI version is actually pretty complex. And matter of fact, if I scroll to the top, you can see we can see the source code here on GitHub. And if I look at these slots.tsx file, you will see that it does a whole lot of things. We are not definitely not going to implement all of this,

that it does a whole lot of things. We are not definitely not going to implement all of this, but I guess the key implementation detail here is if I search for cloneElement is this React cloneElement call React. That cloneElement lets you clone an element and then amplify it with further props that you want to add to it. So the technique here is to clone the child that is passed and then add all the props from the parent.

So the technique here is to clone the child that is passed and then add all the props from the parent element to that child. Okay, let's create yet another UI element manually and to differentiate it from the Ian UI components. I will call it myDashSlots, that's TSX and like I said, we're going to do a pretty minimal implementation here. So export function mySlot and we need to receive the children since

So export function, my slot and we need to receive the children since that's the whole point of passing props to the child. And here we can type this not as a ReactNode but a ReactElement instead. As you can see here, ReactNode can be anything that can be rendered like just a string, but ReactElement is a JSX element which is what we want. And so instead of returning some markup like copilot tries to hint to us, we are going to return cloneElement,

And so instead of returning some markup like copilot tries to hint to us, we are going to return cloneElement, which is a function from React. You can see it's imported up here and we need to tell what elements we want to clone and here we can pass children. You'll see there's an issue with this, but let's go and try to see what happened with just this code. I'm gonna go in the welcome page and go my slot.

I'm gonna go in the welcome page and go my slot. Lemme scroll down and you can see that it's requesting that we pass some children and it's pass a P tag that says hello slot. And there it is a P tag with the hello slot and you can see it's not wrapped in any sort of parent, but as it is it's really unhelpful because say I wanna add a class name text-red-500 to my slot.

because say I wanna add a class name text-red-500 to my slot. You can see already that it's not accepting class name. And as a result nothing is going to change. We don't have any class name to be seen. So to fix this we basically need to receive the extra props like we've done before. So props anything that can be passed to it. And then we can use the second argument of cloneElement. You can see the first element was the element

And then we can use the second argument of clone element. You can see the first element was the element which is our children. And then we can have props here. And so let's have an object and here we can spread the props. Okay, and now hey our hello slot has the class of text-red-500 which has been passed to it. So it's sort of working okay. You can see that our className here is still being warned.

So it's sort of working okay. You can see that our class name here is still being warned as wrong because uh oh because we haven't specified extra props here, we only specify children so we can add and yes, I believe this is what Copilot suggests here, HTML attributes and then HTML element. So basically we accept any HTML attribute for any HTML element because we don't know what elements type the child will be.

for any HTML element because we don't know what elements type the child will be and that should fix it and yep it does, but there is still a problem with our setup. Let's duplicate the hello slot and here's your warning that something is not going to work. And if we look at the UI, we bombed everything. Uh, we have an error message and if we look at the hint here, you can see that we have passed an array of elements, multiple elements,

and if we look at the hint here, you can see that we have passed an array of elements, multiple elements, but really the mySlot is expecting a single element. And this is a problem because cloneElement is looking for one single element, it's not going to be able to clone an array of elements. So we really wanna make sure that there is only ever one element and one only. So we need to ensure there is only one valid child element.

So we need to ensure there is only one valid child element and React is kind enough to give us a utility to check that. So we can do const child equals React.Children.only(children), and then check for the children. And so Children.only will ensure that there is only one single child, which is a valid React element and if not it's going to throw an error.

which is a valid React element and if not it's going to throw an error. So we can use that check to get our child and if this passes we can then clone the child. So I'm going to change children here to child, we still have an error here, but now React has taken over with this check and it says that it's only expecting a single React element. And here you could write some custom codes to provide your own meaningful error message.

And here you could write some custom codes to provide your own meaningful error message. But I think relying on react error message which was pretty clear in that instance makes sense. Alright, a code is starting to look good but there is one more glaring or actually not glaring issue, but a hidden issue that I'm going to reveal. Now I will go back to one child so that the UI shows properly and without any errors. But then look at this, if on the child element here I have a

that the UI shows properly and without any errors. But then look at this, if on the child element here I have a class name of text-green-500, I'm sort of expecting because my child is my own precious child and I want it to be looked after that the text should be green-500 but the text is red-500 and what happens is we clone that element and then we pass it all the props from my slot including the class name which is going to override it to red-500.

and then we pass it all the props from my slot including the class name which is going to override it to red 500. So I'm just talking about class name here. But the same will happen with any attribute that is passed to the child. If the parent was to also have that attribute, it's going to override the child, which is I don't think great. So we can do one more thing here. We are spreading the props past here but turns out that our child element here

We are spreading the props past here but turns out that our child element here because it's a React element, can also have its own props. So what we can do is after we spread the parent props, we can override any clashes by also spreading the child props and TypeScript is not happy here and it's sort of complicated to have the exact correct types.

and it's sort of complicated to have the exact correct types. So I'm going to punt on types here, sorry about that and just use any, but basically what happens here is we clone the child element and then we spread it the props that are passed to the mySlotComponent here and then we are going to also spread the child's own props after that to make them win in case they had been overridden.

after that to make them win in case they had been overridden. So now with that in place, I'm expecting the text to be green-500 because we have cloned that element and then we have overwritten the class name with red-500, but then we have re overridden the override back to green-500 and let's take a look and yep, it works. Alright and to wrap up this episode, let's go back to our MyButton component that we've built from scratch and we are going to implement the mySlot inside of it so

Adding asChild to MyButton11:07

to our MyButton component that we've built from scratch and we are going to implement the mySlot inside of it so that we can render the MyButton with an asChild prop and then have any element that we want inside of it. Alright, let's go in MyButton component and we will make our own implementation of the asChild property, which is optional and a boolean, it is optional because we are going to make it default to false as asChild equals false. And now is where we're going to do the logic

as child equals faults. And now is where we're going to do the logic to determine if we should use a slot or a mySlot or if we should not. So before the return statement, let's go, ah, go away copilot, let me do this myself. const component equals do we have an sChild ? If yes, let's use our mySlot component that I'm going to import and otherwise let's render the default tag,

to import and otherwise let's render the default tag, which should be a button. So this component now will be dynamically rendering either a button element or the my slot component which expects a chart. So all we have to do is replace the hardcoded button element here with the component element. So moment of truth, we going to try to use our my button component.

So moment of truth, we going to try to use our myButton component and basically recreate this SChild variant that renders the SChild. I'm going to delete that example and we are going to use myButton, SChild, verify that it's here and yep it is and it's uppercase because of our compound variant example. But now let's add a <a> tag inside with the text within it.

But now let's add a anchor tag inside with the text within it. This is going to be wrong, this is gonna render a button with an anchor tag inside, which is not great. The button has an anchor tag inside of it, but we are on the right track. And now moment of truth I'm going to add as child, adding as child should indicate to my button that it should clone each child by using the my slot element and then add all the properties, uh,

that it should clone each child by using the mySlot element and then add all the properties, uh, from the myButton to the child. Whew. Let's take a look and I'm going to inspect this and yep, it is an anchor tag. Nice. Let's see if the passing of the props is working. So look, secondary size equals small and className equals font-black to make it very weighty. So close your eyes

to make it very weighty. So close your eyes and we expecting an anchor tag that has the secondary look, a size of small and a thick font weight. You ready? Hey, let's go. This is exactly what I had in mind and so it's working properly and you might notice that while other button have a font weight of medium, the button with the font weight of black has been merged properly with tailwind merge.

weight of medium, the button with the font weight of black has been merged properly with tailwind merge because part of the button, the myButton prop set is this class name that is using the classnames function to merge classes and therefore it has been resolved properly. We cannot find any trace of the font medium because font black has been merged with the rest of the classes. Very cool. Alright, and that is the child prop

with the rest of the classes. Very cool. Alright, and that is the S child prop and the slot utility for you. I hope you enjoyed this lesson. See you in the next one.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟