As your application grows, it becomes more likely to encounter unexpected behavior and bugs; some of those issues could be overcome by using extensions of JavaScript such as Flow or TypeScript, which will help with the typechecking progress of the whole application at compilation time.
However, there is one more important tool for JavaScript React projects, apart from those two, that helps with the troubleshooting process, which is PropTypes.
Introducing PropTypes
PropTypes is a library that provides certain utilities for documenting the types of properties that are being passed down to components.
Once part of the core React module, the PropTypes utilities are now exposed as a standalone library under the npm registry which is called prop-types
.
Installation
In order to install the package, all you have to do is run the following command depending on your package manager of choice:
Safeguarding our props
Now that we’ve successfully installed the library, we can import all of its utilities under an unnamed import, which, in our case, will be called PropTypes
, which is an explicit name.
The imported utilities basically consist of types and methods that allow us to shape the props of the component into certain static or dynamic objects, arrays, etc.
However, in order to link up the types to the component itself, we’ll assign the safeguarded object of types to the propTypes
property of the component.
PropTypes will handle all the dynamic property and the linking under the hood, so there’s a bit of magic involved as well.
As for how everything comes bundled up together, we can check the example below:
import PropTypes from 'prop-types';
const User = ({ name, surname, age }) => {
return (
<div>
<p>Name: {name}</p>
<p>Surname: {surname}</p>
<p>Age: {age}</p>
</div>
)
}
export default User;
User.propTypes = {
name: PropTypes.string,
surname: PropTypes.string,
age: PropTypes.number
};
We have created a User
component that expects 3 props: name
, surname
, and age
. We then render those to the screen to showcase some relevant user information.
And, as you might’ve noticed already, we’ve also used PropTypes to validate the type that we’re expecting each prop to be. We expect name
and surname
to be of type string
and age
to be of type number
. Let’s move to App.js to see how to use our new component.
import User from './User'
function App() {
return (
<div style={{ margin: '50px' }}>
<h1>Current User:</h1>
<User name="Tommy" surname="Smith" age={23} />
</div>
);
}
export default App;
In App.js we’re simply using the User in the JSX and passing relevant props. Let’s see what our application looks like.
Well done! Looks like everything works as it should. There are no errors.
What if we made a mistake when passing props and swapped age and surname because we weren’t paying attention?
import User from './User'
function App() {
return (
<div style={{ margin: '50px' }}>
<h1>Current User:</h1>
<User name="Tommy" surname={23} age="Smith" />
</div>
);
}
export default App;
We can see that PropTypes detected that we passed the wrong type to the props and it then threw an error which we can notice was logged to the console.
By being able to catch such errors we are able to build more predictable applications.
However, we are not limited to strings and numbers; neither as actual types, nor as type options; we can model them to our liking, regardless of them being objects, functions, arrays, or even either of these 3 types.
We could, for example, have a component that takes an array of either numbers or strings and renders them to the screen:
import React from 'react';
import PropTypes from 'prop-types';
const Renderer = ({ itemsToRender }) => {
return (
<ul>
{itemsToRender?.map((itemToRender) => (
<li>{itemToRender}</li>
))}
</ul>
);
};
Renderer.propTypes = {
itemsToRender: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])),
};
export default Renderer;
Let’s try importing and using our component within the App
component without giving it any params and see what happens next.
import './App.css';
import Renderer from './components/Renderer';
function App() {
return (
<div className="App">
<Renderer />
</div>
);
}
export default App;
As expected, if we tried starting our server and opening our Browser, this appears in the console:
Now, let’s try to pass some invalid data down to the Renderer
component and see what happens:
import './App.css';
import Renderer from './components/Renderer';
function App() {
const itemsToRender = [
'Sunday',
15,
23,
true
];
return (
<div className="App">
<Renderer itemsToRender={itemsToRender} />
</div>
);
}
export default App;
The application seems to be doing what it previously did, and that is to display any values it might receive. However, when encountering the 4th value, our application threw us a warning in regards to not having the right props.
You can find more examples here.
You will have to keep in mind that the issues coming from PropTypes will not interrupt the normal flow of the application, but rather help during the development experience.
Usage with TypeScript
Although it seems like they do the same thing, there isn’t actually any overlap between them; TypeScript provides compile time coverage, while PropTypes provide run time coverage.
Not only that, but they serve different purposes altogether:
- TypeScript is useful when writing code, as it will warn you about invalid arguments or props, as well as help you with autocomplete features
- PropTypes is useful when testing how components interact with different data, as well as helping when testing falling components and the reasons for their failures
It is a common question when first dealing with each technology, but the answer is that you can use both libraries together, and it might even be recommended to do so, as it allows for a much higher degree of safety and predictability in your application.
If you wish to learn more about TypeScript and the usage of TypeScript in React applications, be sure to check this article.
Summary
In this article we have been able to go over what the PropTypes library is, what utilities it provides us with, how can those be used, as well as answer the question of whether it is redundant or not to be used alongside TypeScript (Which is not).
I hope you have enjoyed the read and were able to gain some insightful information when it comes to understanding what this library is and how it is used.
In case you feel I’ve missed something, or I haven’t focused enough on a certain aspect of the library, feel free to leave a comment down below where your feedback will be highly appreciated and taken into account.
See you at the next one. Cheers!
💬 Leave a comment