Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React rest parameters syntax vs spread syntax

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.

like image 661
user2456977 Avatar asked Dec 18 '22 05:12

user2456977


2 Answers

{ ...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.

like image 102
Ross Solomon Avatar answered Dec 20 '22 17:12

Ross Solomon


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.

like image 38
lukaleli Avatar answered Dec 20 '22 19:12

lukaleli