127

Looking at this React Router Dom v4 example https://reacttraining.com/react-router/web/example/auth-workflow I see that PrivateRoute component destructures a rest prop like this

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={props => (
    fakeAuth.isAuthenticated ? (
      <Component {...props}/>
    ) : (
      <Redirect to={{
        pathname: '/login',
        state: { from: props.location }
      }}/>
    )
  )}/>
)

I want to be certain that { component: Component, ...rest } means:

From props, get the Component prop and then all other props are given to you, and rename props to rest so you can avoid naming issues with the props passed to the Route render function

Am I right?

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
razor7
  • 2,165
  • 2
  • 19
  • 32
  • 6
    It's a non-standardised yet syntax defined at https://github.com/tc39/proposal-object-rest-spread – zerkms Apr 19 '17 at 00:31

3 Answers3

248

Sorry, I realized my first answer (while hopefully still providing useful/additional context) doesn't answer your question. Let me try again.

You ask:

I want to be certain that { component: Component, ...rest } means:

From props, get the Component prop and then all other props given to you, and rename props to rest so you can avoid naming issues with the props passed to the Route render function

Your interpretation is not quite correct. Based on your thoughts though, it sounds like you're at least aware of the fact that what is happening here amounts to some sort of object destructuring (see second answer and comments there for more clarification).

To give an accurate explanation, let's break down the { component: Component, ...rest } expression into two separate operations:

  1. Operation 1: Find the component property defined on props (Note: lowercase component) and assign it to a new location in state we call Component (Note: capital Component).
  2. Operation 2: Then, take all remaining properties defined on the props object and collect them inside an argument called rest.

The important point is that you're NOT renaming props to rest. (And nor does it have to do with trying to "avoid naming issues with the props passed to the Route render function".)

rest === props;
// => false

You're simply pulling off the rest (hence why the argument is named that) of the properties defined on your props object into a new argument called rest.


Example Usage

Here's an example. Let's assume we have an object `myObj` defined as follows:
const myObj = {
  name: 'John Doe',
  age: 35,
  sex: 'M',
  dob: new Date(1990, 1, 1)
};

For this example, it may help to just think of props as having the same structure (i.e., properties and values) as shown in myObj. Now, let's write the following assignment.

const { name: Username, ...rest } = myObj

The statement above amounts to both the declaration and assignment of two variables (or, I guess, constants). The statement can be thought out as:

Take property name defined on myObj and assign its value to a new variable we call Username. Then, take whatever other properties were defined on myObj (i.e., age, sex and dob) and collect them into a new object assigned to the variable we name rest.

Logging Username and rest to the console would confirm this. We have the following:

console.log(Username);
// => John Doe
console.log(rest);
// => { age: 35, sex: 'M', dob: Mon Jan 01 1990 00:00:00 GMT-0800 (PST) }

Side Note

You may wonder:

Why go through the trouble of pulling off the component property only to rename it Component with a capital letter "C"?

Yeah, it seems pretty trivial. And, while it is a standard React practice, there's a reason all of Facebook's documentation on its framework is written as such. Namely, capitalizing custom components rendered with JSX is less a practice per se than it is a necessity. React, or more properly, JSX is case-sensitive. Custom components inserted without a capitalized first letter are not rendered to the DOM. This is just how React has defined itself to identify custom components. Thus, had the example not additionally renamed the component property that was pulled off of props to Component, the <component {...props} /> expression would fail to render properly.

SNag
  • 17,681
  • 10
  • 54
  • 69
IsenrichO
  • 4,251
  • 3
  • 18
  • 31
  • 12
    You should be the one who can re-write the React Docs, your answer shows a passion in teaching that covers every details for beginner while keep everything simple. – NeoZoom.lua May 01 '21 at 15:22
  • 7
    I just create a bounty for you, this is all I can do, enjoy it :) – NeoZoom.lua May 01 '21 at 15:33
22

It allows you to "spread" all your props in a single concise expression. For example, let's assume the props received by your PrivateRoute component looks like

// `props` Object:
{
  thing1: 'Something',
  thing2: 'Something else'
}

If you wanted to further hand off these items (i.e., thing1 and thing2) down to the nested <Component /> tag and you weren't familiar with the object spread syntax, you might write:

<Component
  thing1={ props.thing1 }
  thing2={ props.thing2 } />

However, the { ...props } syntax obviates such verbosity by allowing you to spread your props object in the same way one might spread an array of values (e.g., [...vals]). In other words, the JSX expression below and that above are exactly equivalent.

<Component { ...props } />
IsenrichO
  • 4,251
  • 3
  • 18
  • 31
  • 2
    While related, don't mix JSX's *spread* syntax with *rest properties*. – Felix Kling Apr 19 '17 at 00:45
  • 5
    *"It allows you to "spread" all your props in a single concise expression."* That's not right. `...rest` in `{ component: Component, ...rest }` *collects* all other properties in the object `rest`. The question is about `...rest`, not `{...props}` – Felix Kling Apr 19 '17 at 00:50
  • 1
    As Felix notes, there is a distinction to be made between the (non-standard) object _spread_ operator in JSX and the _rest_/_spread_ operator as defined in the ECMAScript 2015 specification. For one thing, attempting to write something like `{ ...myObj }` in a non-React environment (_e.g._, the browser console) will throw a `SyntaxError`. Nevertheless, ES6's _rest_/_spread_ provide a useful conceptual framework in which to think of JSX's object _spread_. – IsenrichO Apr 19 '17 at 00:51
8

Lets keep it simple: in JavaScript, if a "key: value" pairs are the same, obj={account:account} is the same as obj={account}. So when passing props from parent to child component:

const Input = ({name,label,error, ...rest}) => {
  return (
    <div className="form-group">
      <label htmlFor={name}>{label}</label>
      <input
        {...rest}
        autoFocus
        name={name}
        id={name}
        className="form-control"
        aria-describedby="emailHelp"
      />
    </div>
  );
};
export default Input;

you will be passing rest of props as:

label={label} placeholder={placeholder} type={type}
robe007
  • 3,523
  • 4
  • 33
  • 59
Yilmaz
  • 35,338
  • 10
  • 157
  • 202