5.4 KiB
id | title | permalink | prev | next |
---|---|---|---|---|
transferring-props | Transferring Props | transferring-props.html | reusable-components.html | forms.html |
It's a common pattern in React to wrap a component in an abstraction. The outer component exposes a simple property to do something that might have more complex implementation details.
You can use JSX spread attributes to merge the old props with additional values:
return <Component {...this.props} more="values" />;
If you don't use JSX, you can use any object helper such as ES6 Object.assign
or Underscore _.extend
:
return Component(Object.assign({}, this.props, { more: 'values' }));
The rest of this tutorial explains best practices. It uses JSX and experimental ES7 syntax.
Manual Transfer
Most of the time you should explicitly pass the properties down. That ensures that you only expose a subset of the inner API, one that you know will work.
var FancyCheckbox = React.createClass({
render: function() {
var fancyClass = this.props.checked ? 'FancyChecked' : 'FancyUnchecked';
return (
<div className={fancyClass} onClick={this.props.onClick}>
{this.props.children}
</div>
);
}
});
React.render(
<FancyCheckbox checked={true} onClick={console.log.bind(console)}>
Hello world!
</FancyCheckbox>,
document.body
);
But what about the name
prop? Or the title
prop? Or onMouseOver
?
Transferring with ...
in JSX
NOTE:
In the example below, the
--harmony
flag is required as this syntax is an experimental ES7 syntax. If using the in-browser JSX transformer, simply open your script with<script type="text/jsx;harmony=true">
. See the Rest and Spread Properties ... section below for more details.
Sometimes it's fragile and tedious to pass every property along. In that case you can use destructuring assignment with rest properties to extract a set of unknown properties.
List out all the properties that you would like to consume, followed by ...other
.
var { checked, ...other } = this.props;
This ensures that you pass down all the props EXCEPT the ones you're consuming yourself.
var FancyCheckbox = React.createClass({
render: function() {
var { checked, ...other } = this.props;
var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
// `other` contains { onClick: console.log } but not the checked property
return (
<div {...other} className={fancyClass} />
);
}
});
React.render(
<FancyCheckbox checked={true} onClick={console.log.bind(console)}>
Hello world!
</FancyCheckbox>,
document.body
);
NOTE:
In the example above, the
checked
prop is also a valid DOM attribute. If you didn't use destructuring in this way you might inadvertently pass it along.
Always use the destructuring pattern when transferring unknown other
props.
var FancyCheckbox = React.createClass({
render: function() {
var fancyClass = this.props.checked ? 'FancyChecked' : 'FancyUnchecked';
// ANTI-PATTERN: `checked` would be passed down to the inner component
return (
<div {...this.props} className={fancyClass} />
);
}
});
Consuming and Transferring the Same Prop
If your component wants to consume a property but also pass it along, you can repass it explicitly checked={checked}
. This is preferable to passing the full this.props
object since it's easier to refactor and lint.
var FancyCheckbox = React.createClass({
render: function() {
var { checked, title, ...other } = this.props;
var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
var fancyTitle = checked ? 'X ' + title : 'O ' + title;
return (
<label>
<input {...other}
checked={checked}
className={fancyClass}
type="checkbox"
/>
{fancyTitle}
</label>
);
}
});
NOTE:
Order matters. By putting the
{...other}
before your JSX props you ensure that the consumer of your component can't override them. In the example above we have guaranteed that the input will be of type"checkbox"
.
Rest and Spread Properties ...
Rest properties allow you to extract the remaining properties from an object into a new object. It excludes every other property listed in the destructuring pattern.
This is an experimental implementation of an ES7 proposal.
var { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x; // 1
y; // 2
z; // { a: 3, b: 4 }
Note:
Use the JSX command-line tool with the
--harmony
flag to activate the experimental ES7 syntax.
Transferring with Underscore
If you don't use JSX, you can use a library to achieve the same pattern. Underscore supports _.omit
to filter out properties and _.extend
to copy properties onto a new object.
var FancyCheckbox = React.createClass({
render: function() {
var checked = this.props.checked;
var other = _.omit(this.props, 'checked');
var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
return (
React.DOM.div(_.extend({}, other, { className: fancyClass }))
);
}
});