Qué es PropTypes y por qué usarla?

Typechecking dinámico en componentes de React

Lupo Montero
5 min readApr 19, 2018

La motivación original detrás de este post era responder/aclarar una pregunta concreta de una compañera (Maricarmen Rojas Tinco):

Cuándo usar PropTypes.object vs PropTypes.shape?

Mi respuesta corta (TL;DR) sería algo como:

PropTypes.object solo dice que la propiedad en cuestión es un objeto (cualquier tipo de objeto, sin especificar de qué tipo o qué propiedades tiene), mientras que PropTypes.shape() nos permite indicar qué interfaz esperamos que tenga el objeto (qué propiedades debe tener), así como describir estructuras anidadas.

Me quedé pensando 🤔 cuál sería la mejor forma de explicárselo a alguien que esté aprendiendo React ⚛️… y me puse a hacer un ejemplo… (la velocidad no es una de mis cualidades 🐢) … así que Alexandra Neira Quiroz y Shirley Silvana Suárez Startary me ganaron con sus respuestas 🥇. De todas formas me quedé con ganas de compartir el ejemplo y profundizar un poquito en PropTypes para dar un contexto a las respuestas.

La respuesta larga:

A la hora de querer explicar las diferencias, me pareció pertinente partir por el principio (qué es PropTypes y por qué usarlo) para entender no solo las diferencias a nivel técnico, si no lo que significan y por qué quisieras usar una en vez de la otra.

Y para ilustrar, qué mejor que un ejemplo (desde cero) que podamos seguir paso a paso y ejecutar en tu computadora.

Imaginemos que tenemos una aplicación con un componente Profile que espera recibir una propiedad user que sea un objeto que a su vez tiene dos propiedades (name y email). Antes de empezar, creemos una nueva aplicación usando create-react-app y un archivo donde implementar Profile.

# instala `create-react-app` globalmente
npm i -g create-react-app
# crea un nuevo proyecto en la carpeta `./prop-types-example`
create-react-app prop-types-example
# entramos en la carpeta
cd prop-types-example
# Creamos archivo para implementar `Profile`
touch src/Profile.js
# abre código fuente en editor (Atom en este ejemplo)
atom .
# arranca aplicación
yarn start

Re-escribamos ./src/App.js para que sea una función que simplemente renderice el componente Profile dentro de un <div>.

Una implementación naive del componente Profile podría ser así:

En un mundo ideal, esperaríamos que el componente se use algo así:

Ahora, si tratamos de invocar el componente sin pasarle props (directamente <Profile />), veremos un error de sintáxis;

TypeError: props.user is undefined

El error se da porque nuestra implementación de Profile trata de acceder a props.user.name y props.user.email, y obviamente props.user no es un objeto. Este tipo de error, muchas veces se origina en el hecho de que nuestro componente no comunica muy bien qué espera/necesita para hacer su trabajo (no declara una interfaz específica, ni una intención).

Es en este contexto que herramientas como prop-types vienen a echarnos una mano y permitirnos “describir” o “documentar” la interfaz (argumentos de entrada, props en este caso) de nuestros componentes. Esto es importante para entender por qué usar prop-types.

PropTypes ofrece una manera de verificar dinámicamente las props de nuestros componentes, algo parecido a lo que hacen sistemas de verificación estática de tipos (static typechecking) como Flow o TypeScript, pero de forma dinámica y declarativa, y solo aplicada a los componentes. Dinámica en este caso significa que la verificación se hace en runtime (durante la ejecución) y no durante la compilación/transpilación (durante la construcción/build).

Antes de usar PropTypes para mejorar nuestro componente, empecemos por hacer la “firma” (signature en inglés) del componente un poco más declarativa (y defensiva), usando asignación desestructurada y valores por defecto.

Si volvemos a invocar <Profile /> tal cual, sin especificar atributos (props), ahora ya no recibimos un error, pero igual no vemos nada en la pantalla, porque el objeto user toma el valor por defecto {} 😰

Para poder comunicar al usuario de este componente que le están faltando cosas, podemos agregar PropTypes, que no solo agrega código fuente que describe a nuestros componentes 📖👍, sino tambien warnings ⚠️⛑ (advertencias) en la consola. Agreguemos una descripción de las props usando PropTypes, empezando por simplemente decir que user debe ser un objeto.

ℹ️ Si prop-types no está instalado en tu proyecto, lo puedes instalar con yarn add prop-types o npm i --save prop-types, dependiendo de si estás usando yarn o npm como package manager.

Si se invoca el componente sin user o si user fuera un valor que no es un objeto, esta nueva implementación mostraría un warning en la consola. Por ejemplo:

<Profile user=”Jimi Hendrix” />

Esto resultaría en el siguiente warning:

Warning: Failed prop type: Invalid prop `user` of type `string` supplied to `Profile`, expected `object`.

Mucho mejor, pero no nos permite especificar que nuestro objeto debe tener name y email. Qué pasaría si invocamos el componente con un objeto sin las propiedades esperadas?

<Profile user={new Date()} />

Este último ejemplo no mostraría ningún error ni warning, porque new Date() produce un objeto (de tipo fecha). Para poder dar más detalles sobre la forma (shape en inglés) de estos objetos (qué propiedades tienen) tenemos PropTypes.shape():

Esta última versión deja claro en el código fuente que esperamos una prop con el nombre user que sea un objeto, que a su vez contenga name (de tipo string y requerida) y email (también de tipo string y requerida). Como vemos el nivel de detalle es mucho mayor, y recibiremos warnings cuando el objeto no tenga la forma esperada.

Con esta nueva descripción de las props, si tratamos de hacer otra vez:

<Profile user={new Date()} />

Ahora veremos un warning en la consola:

Warning: Failed prop type: The prop `user.name` is marked as required in `Profile`, but its value is `undefined`.

🎉 🎈🚀

Al principio de este post mencionamos que PropTypes.shape() nos permite describir estructuras anidadas, ya que una propiedad de un objeto descrito con PropTypes.shape() puede ser a a su vez un objeto también descrito con PropTypes.shape(). Por ejemplo:

Profile.propTypes = {
user: PropTypes.shape({
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
address: PropTypes.shape({
street: PropTypes.string.isRequired,
street2: PropTypes.string,
town: PropTypes.string,
postCode: PropTypes.string.isRequired,
country: PropTypes.string.isRequired,
}),
}).isRequired,
};

--

--