React Props Typechecking with PropTypes
Why validate props in React
React props, which stands for "properties", are read-only object being send from one component to another. Since Javascript is a dynamic language and doen't have a built-in type checking solution, a component doen't know props sent to it has correct type as it designed for. This can lead to bugs, or even worse, unexpected behaviors in a react app.
Many developers turned to TypeScript
because it is a superset of Javascript and more important, it is strong typed. Fortunately, react has an internal mechanism for props type checking: PropTypes
Installation
Install the prop-types
package via yarn or npm:
yarn add prop-types
and import this package into project files:
import PropTypes from 'prop-types';
Basic prop type validators
According to official docs, these are validators for the basic prop types:
- PropTypes.bool: The prop should be a Boolean
- PropTypes.number: The prop should be a number
- PropTypes.any: The prop can be of any data type
- PropTypes.string: The prop should be a string
- PropTypes.func: The prop should be a function
- PropTypes.array: The prop should be an array
- PropTypes.object: The prop should be an object
- PropTypes.symbol: The prop should be a symbol
- PropTypes.node: The prop should be anything that can be rendered by React
- ie. numbers, strings, elements can be rendered on screen, but not boolean
- PropTypes.element: The prop should be a React element
- ie. MyConponent
- PropTypes.any: The prop can be anything
Usage
Ensure a prop is an object with specified type of values
MyComponent.propTypes = { objProp: PropTypes.shape({ id: PropTypes.number, name: PropTypes.string, productSize: PropTypes.oneOf(['S', 'M', 'L']), birthdate: PropTypes.instanceOf(Date), })}
Stric prop object matching
Component.propTypes = { objProp: PropTypes.exact({ name: PropTypes.string, age: PropTypes.number, })}
Make a prop required
MyComponent.propTypes = { propOne: PropTypes.any.isRequired, PropTwo: PropTypes.func.isRequired, objProp: PropTypes.Shape().isRequired}
Ensure a component has a single child
MyComponent.propTypes = { children: PropTypes.element.isRequired}
Ensure a prop to be an instance of a particular class
MyComponent.propTypes = { oneProp: PropTypes.instanceOf(MyClass)}
Full use cases from offical docs
import PropTypes from 'prop-types';MyComponent.propTypes = { // You can declare that a prop is a specific JS type. By default, these // are all optional. optionalArray: PropTypes.array, optionalBool: PropTypes.bool, optionalFunc: PropTypes.func, optionalNumber: PropTypes.number, optionalObject: PropTypes.object, optionalString: PropTypes.string, optionalSymbol: PropTypes.symbol, // Anything that can be rendered: numbers, strings, elements or an array // (or fragment) containing these types. optionalNode: PropTypes.node, // A React element. optionalElement: PropTypes.element, // A React element type (ie. MyComponent). optionalElementType: PropTypes.elementType, // You can also declare that a prop is an instance of a class. This uses // JS's instanceof operator. optionalMessage: PropTypes.instanceOf(Message), // You can ensure that your prop is limited to specific values by treating // it as an enum. optionalEnum: PropTypes.oneOf(['News', 'Photos']), // An object that could be one of many types optionalUnion: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.instanceOf(Message) ]), // An array of a certain type optionalArrayOf: PropTypes.arrayOf(PropTypes.number), // An object with property values of a certain type optionalObjectOf: PropTypes.objectOf(PropTypes.number), // An object taking on a particular shape optionalObjectWithShape: PropTypes.shape({ color: PropTypes.string, fontSize: PropTypes.number }), // An object with warnings on extra properties optionalObjectWithStrictShape: PropTypes.exact({ name: PropTypes.string, quantity: PropTypes.number }), // You can chain any of the above with `isRequired` to make sure a warning // is shown if the prop isn't provided. requiredFunc: PropTypes.func.isRequired, // A required value of any data type requiredAny: PropTypes.any.isRequired, // You can also specify a custom validator. It should return an Error // object if the validation fails. Don't `console.warn` or throw, as this // won't work inside `oneOfType`. customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error( 'Invalid prop `' + propName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }, // You can also supply a custom validator to `arrayOf` and `objectOf`. // It should return an Error object if the validation fails. The validator // will be called for each key in the array or object. The first two // arguments of the validator are the array or object itself, and the // current item's key. customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) { if (!/matchme/.test(propValue[key])) { return new Error( 'Invalid prop `' + propFullName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } })};