Appendix A: PropTypes
PropTypes
are a way to validate the values that
are passed in through our props
. Well-defined
interfaces provide us with a layer of safety at the run time
of our apps. They also provide a layer of documentation to the
consumer of our components.
We define PropTypes
by passing them as an option
to createClass()
:
const Component = React.createClass({
propTypes: {
// propType definitions go here
},
render: function() {}
});
Classical style
propTypes
Defining
propTypes
in a class-based component using ES6 syntax is slightly different as it needs to be defined as aclass
method on the component. For example:class Component extends React.Component { render() {} } Component.propTypes = {/* definition goes here*/};
The key
of the propTypes
object
defines the name of the prop we are validating, while the
value is the types defined by the
React.PropTypes
object (which we discuss below)
or a custom one through a custom function.
The React.PropTypes
object exports a lot of
validators that cover the most of the cases we'll
encounter. For the not-so common cases, React allows us to
define our own PropType
validators as well.
Validators
The React.PropTypes
object contains a list of
common validators (but we can also define our own. More on
that later).
When a prop
is passed in with an invalid type or
fails the prop type, a warning is passed into the JavaScript
console. These warnings will only be shown in
development mode, so if we accidentally deploy our app into
production with an improper use of a component, our users
won't see the warning.
The built in validators are:
-
string
-
number
-
boolean
-
function
-
object
-
shape
-
oneOf
-
instanceOf
-
array
-
arrayOf
-
node
-
element
-
any
-
required
string
To define a prop as a string, we can use
React.PropTypes.string
.
const Component = React.createClass({
propTypes: {
name: React.PropTypes.string
},
// ...
})
To pass a string as a prop we can either just pass the string
itself as an attribute or use the {}
braces to
define a string variable. These are all
functionally equivalent:
<Component name={"Ari"} />
<Component name="Ari" />
number
To specify a prop should be a number, we can use
React.PropTypes.number
.
const Component = React.createClass({
propTypes: {
totalCount: React.PropTypes.number
},
// ...
})
Passing in a number, we must pass it as a JavaScript value or the value of a variable using braces:
var x = 20;
<Component totalCount={20} />
<Component totalCount={x} />
boolean
To specify a prop should be a boolean (true or false), we can
use React.PropTypes.bool
.
const Component = React.createClass({
propTypes: {
on: React.PropTypes.bool
},
// ...
})
To use a boolean in a JSX expression, we can pass it as a JavaScript value.
var isOn = true;
<Component isOn={true} />
<Component isOn={false} />
<Component on={isOn} />
A trick for using booleans to show or hide content is to use the
&&
expression.For instance, inside our
Component.render()
function, if we want to show content only when theon
proptype is true, we can do so like:render: function() { return ( <div> {this.props.on && <p>This component is on</p>} </div> ) }
function
We can pass a function as a prop
as well. To
define a prop to be a function, we can use
React.PropTypes.func
. Often times when we write a
Form
component, we'll pass a function as a
prop to be called when the form is submitted (i.e.
onSubmit()
). It's common to define a prop as
a required function on a component.
const Component = React.createClass({
propTypes: {
onComplete: React.PropTypes.func
},
// ...
})
We can pass a function as a prop by using the JavaScript expression syntax, like so:
const x = function(name) {};
const fn = value => alert("Value: " + value);
<Component onComplete={x} />
<Component onComplete={fn} />
object
We can require a prop should be a JavaScript object through
the React.PropTypes.object
:
const Component = React.createClass({
propTypes: {
user: React.PropTypes.object
},
// ...
})
Sending an object through as a prop, we'll need to use
the JavaScript expression {}
syntax:
const user = {
name: 'Ari'
}
<Component user={user} />
<Component user={ {name: 'Anthony'} } />
object shape
React allows us to define the shape of an object we expect to
receive by using React.PropTypes.shape()
. The
React.PropTypes.shape()
function accepts an
object with a list of key-value pairs that dictate the keys an
object is expected to have as well as the value type:
const Component = React.createClass({
propTypes: {
user: React.PropTypes.shape({
name: React.PropTypes.string,
friends: React.PropTypes.arrayOf(React.PropTypes.object),
age: React.PropTypes.number
})
},
// ...
})
multiple types
Sometimes we don't know in advance what kind a particular
prop will be, but we can accept one or other type. React gives
us the propTypes of oneOf()
and
oneOfType()
for these situations.
Using oneOf()
requires that the propType be a
discrete value of values, for instance to require a component
to specify a log level value:
const Component = React.createClass({
propTypes: {
level: React.PropTypes.oneOf(['debug', 'info', 'warning', 'error'])
},
// ...
})
Using oneOfType()
says that a prop can be one of
any number of types. For instance, a phone number may either
be passed to a component as a string or an integer:
const Component = React.createClass({
propTypes: {
phoneNumber: React.PropTypes.oneOfType([
React.PropTypes.number,
React.PropTypes.string
])
},
// ...
})
instanceOf
We can dictate that a component must be an instance
of a JavaScript class using
React.PropTypes.instanceOf()
as the value of the
propType:
const Component = React.createClass({
propTypes: {
user: React.PropTypes.instanceOf(User)
},
// ...
})
We'll use the JavaScript expression syntax to pass in a particular prop.
const User = function(name) {
this.name = name;
return this;
};
const ari = new User('Ari');
<Component user={ari} />
array
On occasion we'll want to send in an array as a prop. To
set an array, we'll use the
React.PropTypes.array
as the value.
const Component = React.createClass({
propTypes: {
authors: React.PropTypes.array
},
// ...
})
Sending an object through as a prop, we'll need to use
the JavaScript expression {}
syntax:
const users = [
{name: 'Ari'}
{name: 'Anthony'}
];
<Component authors={[{name: 'Anthony'}]} />
<Component authors={users} />
array of type
React allows us to dictate the type of values each member of
an array should be using
React.PropTypes.arrayOf()
.
const Component = React.createClass({
propTypes: {
authors: React.PropTypes.arrayOf(React.PropTypes.object)
},
// ...
})
We'll use the JavaScript expression syntax
{}
to pass in an array:
const users = [
{name: 'Ari'}
{name: 'Anthony'}
];
<Component authors={[{name: 'Anthony'}]} />
<Component authors={users} />
node
We can also pass anything that can be rendered, such as
numbers, string, DOM elements, arrays, or fragments that
contain them using the React.PropTypes.node
.
const Component = React.createClass({
propTypes: {
icon: React.PropTypes.node
},
// ...
})
Passing a node as a prop is straightforward as well.
Passing a node as a value is often useful when requiring a
component to have children or setting a custom element. For
instance, if we want to allow our user to pass in either the
name of an icon or a custom component, we can use the node
propType
.
const icon = <FontAwesomeIcon name="user" />
<Component icon={icon} />
<Component icon={"fa fa-cog"} />
element
React's flexibility allows us to pass another React
element in as a prop as well by using the
React.PropTypes.element
:
const Component = React.createClass({
propTypes: {
customHeader: React.PropTypes.element
},
// ...
})
We can build our components so that the interface they allow
our users to specify a custom component. For instance, we
might have a <List />
component who's
responsibility is to output a list of elements. Without custom
components, we would have to build a separate
<List />
React component for each type of
list we want to render (which might be appropriate, depending
on the behavior of the element). By passing a component type,
we can reuse the <List />
component.
For instance, a list component might look like:
const List = React.createClass({
propTypes: {
listComponent: PropTypes.element,
list: PropTypes.array
},
renderListItem: function(item, i) {
const Component = this.props.listComponent || "li";
return React.createElement(Component, this.props, item)
},
render: function() {
const list = this.props.list;
return (
<ul>
{list.map(this.renderListItem)}
</ul>
)
}
});
We can use this list component with or without specifying a custom component:
const Item = React.createClass({
render: function() {
return (
<div>{this.props.children}</div>
)
}
})
<List list={[1, 2, 3]} />
<List list={[1, 2, 3]} listComponent={Item} />
any type
React also allows us to specify that a prop must be present,
regardless of it's type. We can do this by using the
React.PropTypes.any
validator.
const Component = React.createClass({
propTypes: {
mustBePresent: React.PropTypes.any
},
// ...
})
Optional & required props
All props are considered optional unless otherwise specified.
To require a prop be passed to a component and validated, we
can append every propType validation with
.isRequired
.
For instance, if we must have a function to get called after
some action when a Loading
component has
completed, we can specify it like this:
const Loading = React.createClass({
propTypes: {
// Optional props:
onStart: React.PropTypes.func,
// Required props:
onComplete: React.PropTypes.func.isRequired,
name: React.PropTypes.string.isRequired
},
// ...
})
custom validator
React allows us to specify a custom validation function for all of the other situations where the default validation functions don't cover it. In order to run a write a custom validation we'll specify a function that accepts 3 arguments:
- The
props
passed to the component - The
propName
being validated -
The
componentName
we're validating against
If our validation passes, we can run through the function and
return anything. The validation function will only fail if an
Error
object is raised (i.e.
new Error()
).
For instance, if we have a loader that accepts validated users, we can run a custom function against the prop.
const Component = React.createClass({
propTypes: {
user: function(props, propName, componentName) {
const user = props[propName];
if (!user.isValid()) {
return new Error('Invalid user');
}
}
},
// ...
})