15

If the o object contains a key/value pair of: foo: 'bar' can I depend on these outcomes?:

// foo will be 'bar'
<MyComponent
    foo='should not override'
    {...o}
  />

// foo will be 'overridden'
<MyComponent
    {...o}
    foo='overridden'
  />

In other words, is the ordering of properties while using the spread operator significant?

Andrew Li
  • 55,805
  • 14
  • 125
  • 143
Jonathan.Brink
  • 23,757
  • 20
  • 73
  • 115

3 Answers3

10

yes, it is. It works exactly as your example says

You example is translated into:

// foo will be 'bar'
<MyComponent
    {/* ...other 'o' keys/values...*/}
    foo='should not override'
    {/* ...other 'o' keys/values...*/}
    foo='bar'
/>

// foo will be 'overridden'
<MyComponent
    foo='bar'
    {/* ...other 'o' keys/values...*/}
    foo='overridden'
    {/* ...other 'o' keys/values...*/}
/>

And therefore, it overrides always the last one

Mayday
  • 4,680
  • 5
  • 24
  • 58
7

Yes, ordering does matter. The exact reason is how Babel transpiles the JSX. You can see this in the Babel REPL:

<MyComponent foo="should not override" {...o}>

</MyComponent>

Becomes:

React.createElement(MyComponent, _extends({ foo: "overridden" }, o));

Where _extends is just Object.assign, or if the browser doesn't support it, _extends is functionally the same. Per the MDN documentation:

Properties in the target object will be overwritten by properties in the sources if they have the same key. Later sources' properties will similarly overwrite earlier ones.

(Emphasis is mine). Thus, when Object.assign is used to pass props to the component, the target is { foo: "overridden" } and the source is o. Since foo exists in both the target and the source, foo in the target is overridden. This also applies to:

<MyComponent {...o} foo="overridden">

</MyComponent>

Here, the JSX is transpiled to the opposite:

React.createElement(MyComponent, _extends({}, o, { foo: "overriden" }));

It's a bit different because here, the target is an empty object, but the latter half of the quote from MDN applies. The sources here are o and { foo: "overridden" }. Since foo exists in both sources, the foo in source { foo: "overridden" } overwrite's the foo from o.

Andrew Li
  • 55,805
  • 14
  • 125
  • 143
2

Check out this sandboxed proof:

https://codesandbox.io/s/Q1GMx9KM9

As you can see it behaves exactly as you theorized in your question.

EDIT SO Snippet:

class MyComponent extends React.Component {
  render() {
    return <div>{this.props.foo}</div>
  }
}

const styles = {
  fontFamily: 'sans-serif',
  textAlign: 'center',
};

const o = { foo: 'bar' };

const App = () =>
  <div style={styles}>
    <h2>Spreading after explicit property</h2>
    <MyComponent foo="will be overriden" {...o} />
    <h2>Spreading before explicit property</h2>
    <MyComponent {...o} foo="was overriden" />
  </div>;

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Freeman Lambda
  • 3,567
  • 1
  • 25
  • 33
  • 1
    Thank you for providing a runnable implementation. Running your code helped more than reading the whole discussion :) – sedran Sep 30 '21 at 10:03