Question in short:
What is the difference between these two versions:
const A = ({ children, ...rest }) => React.cloneElement(children, rest);
vs
const B = ({ children, ...rest }) => React.cloneElement(children, {...rest});
Both versions seem to work the same way.
This version does not work:
const C = ({ children, ...rest }) => React.cloneElement(children, { rest });
Question in more detail:
...rest vs rest
...rest
that is declared in the component function definition acts as rest parameters syntax represents the rest of the props passed down to the component .
eg: const B = ({ children, ...rest })
However, ...rest
that is passed as an argument:
eg: React.cloneElement(children, {...rest})
represents spread syntax. Here it seems we are also simply cloning children element with the same props from the component function definition.
But how does component A work?
const A = ({ children, ...rest }) => React.cloneElement(children, rest);
How did we get from ...rest
to rest
?
Finally, why does it NOT work when surrounded with brackets like component C
I've read documentation on React and documentation on ES6 but there isn't great documentation for use of them together
using React APIs.
{ ...rest }
is effectively doing a shallow copy of the original rest
object. It keeps the original shape, but it's a different reference point in memory.
rest
also obviously keeps the same shape, because it's the same object. You're simply passing the reference.
{ rest }
doesn't work because it creates a new object and assigns the old rest
object as a value to the key "rest"
.
As an example to the above 3 scenarios, say your original props
shape is as follows:
{
children: <div>,
label: 'Foo',
value: 5
};
After creating an object and passing it through the constructor ({ children, ...rest }
), children
has been separated from rest
, leaving you with the following object:
{
label: 'Foo',
value: 5
};
With { ...rest }
, the object remains the same shape:
{
label: 'Foo',
value: 5
};
With rest
, the object remains not only the same shape, but literally the same object:
{
label: 'Foo',
value: 5
};
With { rest }
, you're assigning the old rest
to the "rest"
key:
{
rest: {
label: 'Foo',
value: 5
},
};
Since the component expects the first shape, and doesn't expect properties under a rest
key, this last case fails as you've observed.
Your function declaration actually uses destructuring along with rest parameters. In your example
const A = ({ children, ...rest }) => ...
function A takes an object as a parameter and destructures it into separate variables, eg.:
const A = ({ a, ...rest }) => { console.log('a', a, 'rest', rest) }
A({a: 1, b: 2, c: 'hello world'})
// a 1 rest Object {b: 2, c: "hello world"}
As you can see above variable a
is picked from the object and assigned to separate variable and spread operator is used to get rest of the object properties and resolves to the object with these properties.
So what happens in your 3 cases?
const A = ({ children, ...rest }) => React.cloneElement(children, rest);
This one passes object to React.cloneElement with all object properties without children
property.
const B = ({ children, ...rest }) => React.cloneElement(children, {...rest});
This one is equivalent to the first one- it spreads the object properties to the object. This operation is actually redundant.
const C = ({ children, ...rest }) => React.cloneElement(children, { rest });
This one creates new object with rest
property that has rest object assigned to it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With