react revisitado: 04. Propiedades

Cómo comunicar los componentes de React entre sí mediante propiedades (properties)

George Saru

archivado en: JavaScript / 10 Mayo, 2017 / taller:

Seguimos con el taller dedicado a React 15.5.

En la entrada anterior refactorizamos el componente principal de la home para incluir un state con los ingresos y los gastos, que renderizamos después en un listado. Dado que la lista de gastos y la de ingresos son muy parecidas, solo cambian los datos de partida, parece buena idea que hagamos un componente para esta parte. Lo podemos llamar ListEconomy o como nos parezca bien.

Primero preparamos el componente, que de momento no tendrá nada, y lo guardamos en el mismo directorio donde tenemos el componente ViewMain, que en mi caso se llama igual, view-main.

src/components/view-main/list-economy.js

import React from 'react';

const ListEconomy = () => {

return (

<article>

Listado: todo

</article>

);

};

export default ListEconomy;

Lo añadimos al listado de componentes que tenemos en el directorio components.

src/components/index.js

import MainHeader from './commons/main-header';

import MainFooter from './commons/main-footer';

import ViewMain from './view-main/view-main';

import ListEconomy from './view-main/list-economy';

export {

MainHeader,

MainFooter,

ViewMain,

ListEconomy

}

Luego lo importamos en el componente ViewMain...

src/components/view-main/view-main.js

import React, { Component } from 'react';

import { MainHeader, MainFooter, ListEconomy } from '../../components';

class ViewMain extends Component {

...

Y finalmente refactorizamos el render de ViewMain para incluirlo en lugar de los listados.

src/components/view-main/vie+w-main.js

...

render() {

return (

<div className='view-main'>

<MainHeader />

{/* Aquí irán los ingresos */}

<ListEconomy />

{/* Aquí irán los gastos */}

<ListEconomy />

<p>

{( this.state.subtotalOutputs > this.state.subtotalInputs ) ?

<span className='warning'>Cuidado, vamos mal.</span> :

<span className='info'>Todo correcto.</span>

}

</p>

<MainFooter />

</div>

);

}

Una vez montada la estructura, vamos a ver cómo pasamos información entre los distintos componentes.

Comunicación entre componentes

Como ocurre en polymer, angular y otros frameworks similares, la comunicación entre componentes se produce de arriba hacia abajo mediante atributos indicados en el tag del componente hijo que se encuentra en el padre, los cuales se convierten en propiedades del hijo.

Si el componente hijo está definido mediante una función, las propiedades se recogen directamente en los parámetros que recibe.

const SubComponente = (props) => {

return (

<article>

{props.foo} - {props.bar}

</article>

);

};

Y si nos queremos ahorrar el props, podemos jugar con la destructuración de ecmaScript 6.

const SubComponente = ({foo, bar}) => {

return (

<article>

{foo} - {bar}

</article>

);

};

Si el componente está escrito como clase, entonces estas propiedades se reciben en el método render y hay que referirse a ellas anteponiendo la partícula this, pues forman parte de la clase.

class SubComponente extends Component {

render(props) {

return (

<article>

{this.props.foo} - {this.props.bar}

</article>

);

}

}

Comprendido esto, volvamos a nuestra aplicación.

ListEconomy

Nuestro componente necesita tres propiedades: el subtitular, un array con los ítems y el subtotal. Se los pasamos desde el componente padre (ViewMain) a los dos hijos (ListEconomy).

src/components/view-main/view-main.js

...

{/* Ingresos */}

<ListEconomy subtitle='Ingresos' items={this.state.inputs} subtotal={this.state.subtotalInputs} />

{/* Gastos */}

<ListEconomy subtitle='Gastos' items={this.state.outputs} subtotal={this.state.subtotalOutputs} />

...

Y ahora lo único que debemos hacer es preparar en el subcomponente un fragmento de JSX similar al que teníamos antes, pero cambiando los this.state... por las propiedades que va a recibir.

src/components/view-main/list-economy.js

const ListEconomy = ({subtitle, items, subtotal}) => {

return (

<article>

<h5>{ subtitle }</h5>

<ul>

{ items.map((item) =>

<li key={item.id}> {item.description}: {item.quantity}</li>

) }

</ul>

<p>

Subtotal ingresos: { subtotal }

</p>

</article>

);

};

prop-types

Como es sabido, en javaScript las variables no están tipadas. Es decir, podemos empezar definiendo una variable como un string...

let foo = 'Uka Lele';

...y más adelante convertirla en un number o cualquier otro tipo.

// Ahora foo es un número.

foo = 5;

// Y ahora lo convertimos en un array y no pasa nada.

foo = ['naranjas', 'mandarinas'];

Esta característica nos puede parecer cómoda, sobre todo si tenemos poco tiempo para terminar algo, pero en realidad es algo peligrosa, pues la integridad de los datos, que siempre mantengan su naturaleza inicial, evita que se produzcan bugs inesperados.

Por ejemplo, en nuestra aplicación estamos comprobando si los gastos son mayores que los ingresos para mostrar un mensaje u otro y eso lo podemos hacer sin miedo porque ambos valores son números, si fueran cadenas, strings, nos encontraríamos con rarismos como este pues lo que se compara son caracteres.

let gastos = '10000';

let ingresos = '200';

console.log ( gastos > ingresos ) // false!!!

Para evitar este tipo de cosas, en la familia de angular 2 se incorporó typeScript, una especie de extensión de javaScript que permite tiparlo, que también se podría utilizar con React, pero en este caso no hace falta meterse en este berenjenal, pues existe una librería muy buena para comprobar qué le está llegando a cada componente: prop-types.

La instalamos con yarn o npm.

yarn add prop-types --save

Y luego la importamos en los componentes que la vayamos a usar de la misma manera que importamos React (cuando no se especifica una ruta, se busca por defecto en el directorio node_modules).

import PropTypes from 'prop-types';

Ahora es tan sencillo como añadir la propiedad propTypes al componente, ya esté definido con una clase o con una función, y en ella ir indicando el tipo de cada propiedad.

class MiComponente extends React.Component {

render() {

return (

<h1>Propiedad de tipo string: {this.props.foo}</h1>

);

}

}

MiComponente.propTypes = {

foo: PropTypes.string

};

Se pueden definir todos los tipos de javaScrit:

  • propiedad: PropTypes.array,
  • propiedad: PropTypes.bool,
  • propiedad: PropTypes.func,
  • propiedad: PropTypes.number,
  • propiedad: PropTypes.object,
  • propiedad: PropTypes.string,
  • propiedad: PropTypes.symbol

Como en nuestro caso vamos a recibir un array, un string y un number, de momento podríamos hacer algo así:

...

ListEconomy.propTypes = {

subtitle: PropTypes.string,

items: PropTypes.array,

subtotal: PropTypes.number

}

...

De esta manera, nos cantaría por consola cualquier desajuste. Por ejemplo,  si hubiéramos definido subtotal como array, dado que le estamos pasando un number, nos habría saltado un warning.

 

Además, podemos especificar si una propiedad debe recibirse obligatoriamente añadiendo la expresión isRequired.

propiedad: PropTypes.string.isRequired

Termino con otras dos fórmulas muy útiles: arrayOf que permite definir la naturaleza de los elementos de un array y shape, para desglosar las claves de un objeto. Combinadas en nuestro caso en el array de objetos que son los items podríamos hacer algo así.

ListEconomy.propTypes = {

subtitle: PropTypes.string.isRequired,

items: PropTypes.arrayOf( PropTypes.shape( {

id: PropTypes.string,

description: PropTypes.string,

quantity: PropTypes.number

} ) ),

subtotal: PropTypes.number.isRequired

};

Bueno, pues por hoy vamos a dejarlo aquí.

|| Tags: , ,

Este artículo aún no ha sido valorado.

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Aportar un comentario

*