Understanding Dynamic React components with cloneElement and React.Children

Hey guys, i am following a tutorial from Meta Front End course on coursera and trying to understand lab example they had on creating dynamic react components. What code basically does is creating radio group component and radio option component that can accepts childrens and props. Most of the code i understood, but what puzzles me right now is why we need to include repeating props of:
checked = {checked}
checked = {checked}
in in radio option component:
export const RadioOption = ({ value, checked, onChange, children }) => {
return (
<div className="RadioOption">
<input
id={value}
type="radio"
name={value}
value={value}
onChange={(e) => {
onChange(e.target.value);
}}
checked={checked}
/>
<label htmlFor={value}>{children}</label>
</div>
);
};
export const RadioOption = ({ value, checked, onChange, children }) => {
return (
<div className="RadioOption">
<input
id={value}
type="radio"
name={value}
value={value}
onChange={(e) => {
onChange(e.target.value);
}}
checked={checked}
/>
<label htmlFor={value}>{children}</label>
</div>
);
};
As far as i understood we already assign and modify whatever children is inside of RadioGroup element through adding additionals props like:
checked: child.props.value === selected
checked: child.props.value === selected
in RadioGroup component:
export const RadioGroup = ({ onChange, selected, children }) => {
const RadioOptions = Children.map(children, (child, index) => {
return cloneElement(child, {
checked: child.props.value === selected,
onChange,
key: index,
});
});
return <div className="RadioGroup">{RadioOptions}</div>;
};
export const RadioGroup = ({ onChange, selected, children }) => {
const RadioOptions = Children.map(children, (child, index) => {
return cloneElement(child, {
checked: child.props.value === selected,
onChange,
key: index,
});
});
return <div className="RadioGroup">{RadioOptions}</div>;
};
If i remove checked = {checked} , i am losing ability to choose only one option, but i don't understand why. RadioGroup should clone and map over each child component (being Radio Options) give it checked prop and assign value to it being either true or false depending if selected value is equal to its props.value. I dont really understand the point of adding checked = {checked} and why it doesn't work without it.
6 Replies
dys 🐙
dys 🐙17mo ago
I'm a little confused by your question. Do you think that you should be able to do:
export const RadioGroup = (
({ onChange, selected, children }) => {
const RadioOptions = Children.map(
children, (child, index) => (
cloneElement(child, {
checked: child.props.value === selected,
onChange,
key: index,
})
)
)
return (
<div className="RadioGroup">
{RadioOptions}
</div>
)
}
)
export const RadioGroup = (
({ onChange, selected, children }) => {
const RadioOptions = Children.map(
children, (child, index) => (
cloneElement(child, {
checked: child.props.value === selected,
onChange,
key: index,
})
)
)
return (
<div className="RadioGroup">
{RadioOptions}
</div>
)
}
)
Then omit the checked property from the input?
export const RadioOption = (
({ value, onChange, children }) => (
<div className="RadioOption">
<input
id={value}
type="radio"
name={value}
onChange={({ target: { value } }) => {
onChange(value);
}}
{...{ value }}
/>
<label htmlFor={value}>{children}</label>
</div>
)
)
export const RadioOption = (
({ value, onChange, children }) => (
<div className="RadioOption">
<input
id={value}
type="radio"
name={value}
onChange={({ target: { value } }) => {
onChange(value);
}}
{...{ value }}
/>
<label htmlFor={value}>{children}</label>
</div>
)
)
Arto
ArtoOP17mo ago
@dys 🐙 hey, thank you for responding. Yes, i think (if i am actually thinking about that correctly) that if i clone and then assign a new prop object to the each RadioOptions
checked: child.props.value === selected
checked: child.props.value === selected
i already create RadioOptions with checked prop (in RadioGroup component). So why do i need to include this checked prop again in child component RadioOption
checked: {checked}
checked: {checked}
cause otherwise code doesn't behave as i expect it to
Daryl
Daryl17mo ago
Hi! When you create a React Element with cloneElement, you pass an object as the second argument: props. These props won't be part of the new element attributes, that is not handle just by cloneElement. If you don't set the checked attribute with value props.checked, it's like passing a prop that is not used by the component. To avoid adding checked in the RadioOption component, you could do this:
const RadioOption = ({
value,
onChange,
children,
...rest
}) => {
return (
<input
// extra stuff
{...rest} // assuming that all the attrs here won't create problems
/>
)
}
const RadioOption = ({
value,
onChange,
children,
...rest
}) => {
return (
<input
// extra stuff
{...rest} // assuming that all the attrs here won't create problems
/>
)
}
You need to consider that this is not safe and it might make debugging really annoying. Take a look to the docs recommendation: https://react.dev/reference/react/cloneElement.
Arto
ArtoOP17mo ago
Hey, thank you for the answer! If I understand that correctly: the second argument that I pass , as an object, to the cloneElement is basically only "read-only" props , but the function itself isn't creating element attributes on them ? So I pass value to the specified elements, while i need to define them in children component (RadioOption in that example). I think i also misunderstood another concept here, is that i mix up attributes and props here, thinking that I pass automatically key-value pair from cloneElement function.
Daryl
Daryl17mo ago
Yeah, cloneElement won't set attributes for the element, it will only pass the props. I'm not sure if I was clear enough. I'd recommend to check the documentation, but feel free to ask any questions when needed.
Arto
ArtoOP17mo ago
Thank you a lot, I think I understood the difference here.
Appreciate your time explaining it !
Want results from more Discord servers?
Add your server