About Theo's take of not destructuring props

I can't remember in what video exactly, but I heard Theo saying something about using component props without destructuring them. I gave it a go but stumbled on some scenarios where I'm really scratching my head to know how to do properly without destructuring. Basically it's a matter of default values and prop forwarding 1. Default Values
export const NewComponent: React.FC<{ testColor: string } & React.ComponentPropsWithoutRef<"input">> = (props) => {
return (
<label>
<p style={{ background: props.testColor }}>Label</p>
<input {...props} />
</label>
);
};
export const NewComponent: React.FC<{ testColor: string } & React.ComponentPropsWithoutRef<"input">> = (props) => {
return (
<label>
<p style={{ background: props.testColor }}>Label</p>
<input {...props} />
</label>
);
};
I know only of "defaultProps", but not only it is deprecated, but also adds another layer of indirection by having to read the code below at another place
NewComponent.defaultProps = {
testColor: "red"
}
NewComponent.defaultProps = {
testColor: "red"
}
2. Prop Forwarding In the example I gave before, there is a problem: even though I only want to use testColor in the p element, when I use ...props in the input element, I'm passing testColor as well, which throws a warning. How it would be with destructured props
export const NewComponent: React.FC<{ testColor: string } & React.ComponentPropsWithoutRef<"input">> = ({
testColor = "red",
...rest
}) => {
return (
<label>
<p style={{ background: testColor }}>Label</p>
<input {...rest} />
</label>
);
};
export const NewComponent: React.FC<{ testColor: string } & React.ComponentPropsWithoutRef<"input">> = ({
testColor = "red",
...rest
}) => {
return (
<label>
<p style={{ background: testColor }}>Label</p>
<input {...rest} />
</label>
);
};
Also, the React documentation itself only states destructuring as the way to pass default values to components: https://react.dev/learn/passing-props-to-a-component#specifying-a-default-value-for-a-prop
Passing Props to a Component – React
The library for web and native user interfaces
7 Replies
Jotas
Jotas•4mo ago
which throws a warning.
What warning? if you need default values "without destructuring" you can just use code before return like
export const NewComponent: React.FC<{ testColor: string } & React.ComponentPropsWithoutRef<"input">> = (props) => {
const testColor = props.testColor ?? 'red'
return (
<label>
<p style={{ background: testColor }}>Label</p>
<input {...props} />
</label>
);
};
export const NewComponent: React.FC<{ testColor: string } & React.ComponentPropsWithoutRef<"input">> = (props) => {
const testColor = props.testColor ?? 'red'
return (
<label>
<p style={{ background: testColor }}>Label</p>
<input {...props} />
</label>
);
};
Paulo Martins
Paulo Martins•4mo ago
Thanks for answering! I saw something like this in the example of how default values work from behind the scenes, in the React doc link I sent (the "Pitfall" section), and it's pretty much this, but the issue below persists
Warning: React does not recognize the testColor prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase testcolor instead. If you accidentally passed it from a parent component, remove it from the DOM element.
Basically, this happens because props has not only the attributes for input, but the new prop testColor as well, which is being passed to the <input> element even though it does nothing in this case
Jotas
Jotas•4mo ago
oh i see, yeah in that case destructuring is superior imo and i do it all the time lol however i believe you can just cast it if you really need to. <input {...props as React.ComponentPropsWithoutRef<"input">} /> And yeah, youre absolutely correct about why the error happens, casting should bypass this without having to resort to an any type. you can also separate testColor and the component props with something like React.FC<{ testColor: string, inputProps: React.ComponentPropsWithoutRef<"input"> }> then <input {...props.inputProps} /> which might be the best practice for that case (you can see that pattern all arround ui packages like Material UI)
usmanabdurrehman
usmanabdurrehman•4mo ago
Without the proper context of why he said it, cant really say much but I use prop spreading all the time. 🥳
Paulo Martins
Paulo Martins•4mo ago
Hey! Sorry for the late response. So, I was thinking about this, and it may be correct solution indeed. Like, Using the "...rest" approach with destructuring may lead to some confusion on where the props are going when you type them while using the component. The only downside is that you loose the attribute syntax when you want to use those inputProps, but it makes sense. Gonna make an example below to better clarify what I'm saying, and gonna test using this approach for a couple weeks to see how it goes, but thanks for the help!
tylerlaws0n
tylerlaws0n•4mo ago
FWIW shadcn/ui uses this pattern repeatedly. Not saying that doesn’t mean that it still might not be optimal, but spreading props for components like ones you’d see in a UI library is super common. Would be interesting to hear a reason when/why not to do this though.
Paulo Martins
Paulo Martins•4mo ago
Yeah, I'm doing it more as an experiment. I still very much prefer the spreading approach, but I'm gonna try without it and see how it works. Maybe there are some insights that could help with better prop definition in general. Some things I've been thinking lately are concepts like: "Is it interesting for the user of the component to have to know its inner workings? Or should it act more like a black box?", "What are the advantages of making a closed component vs a component with subcomponents that the user has to 'Lego'-it together", "When to use children prop vs creating another prop with ReactNode type" and "Should primitives be treated differently regarding their type definitions?". I know a lot of this depends on the use case, but I just want to have a mental model on what to do in each situation (perhaps make a flow chart also?)
Want results from more Discord servers?
Add your server