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');
}
}
},
// ...
})