Novedades de React16

Descubre qué novedades trae la versión 16 de React

Image for post
Image for post

React 16 llega para confirmar su madurez como librería de referencia en lo que a desarrollo frontend se refiere. A lo largo de este post os haré un resumen de sus principales novedades, las cuales introducen nuevas formas de hacer determinadas cosas y aportan funcionalidades muy interesantes que harán que améis React un poco más. Allá vamos!

Error boundaries

React 16 introduce un nuevo concepto conocido como error boundary. Básicamente son componentes que capturan las excepciones producidas en JavaScript dentro de sus componentes hijos, lo cual nos permite loggear los errores y mostrar mensajes de error en vez del árbol de error que se genera cuando la excepción no es capturada.

Puesto que estos error boundaries son componentes de React, la manera de crearlos será añadir el nuevo método componentDidCatch(error, info): el cual actúa del mismo modo que la expresión catch pero para componentes. Esto significa que en el momento en que se produzca una excepción dentro de un componente hijo del componente que hayamos definido como ErrorBoundary, el método componentDidCatch será ejecutado. Eso sí, es importante saber que este método no captura posibles errores dentro del propio componente, por lo que deberemos asegurarnos de que no se producen fallos durante su método render o en la ejecución de otras tareas.

Un ejemplo de cómo podríamos crear un Error Boundary sería el siguiente:

class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
this.setState({ hasError: true });
logError(error, info);
}
render() {
if (this.state.hasError) {
return <h1>Algo fue mal.</h1>;
}
return this.props.children;
}
}

Y para usarlo bastaría con:

<ErrorBoundary>  
<AnyComponent />
</ErrorBoundary>

Nuevos tipos que podremos devolver dentro de un render

Con React 16 por fin ya no será necesario envolver en un div los componentes que queramos devolver dentro de una función render. Ahora ya es posible devolver un array de componentes directamente (empleando una key distinta para cada uno de ellos) y una cadena de texto, lo cual hace aún más cómodo trabajar con React:

render() {
return [
<li key="first">First</li>,
<li key="second">Second</li>,
<li key="third">Third</li>,
];
}

Atributos especiales en el DOM

Hasta ahora, React omitía todos aquellos atributos que no reconociese a la hora de imprimir un componente. Es decir, si en nuestro código teníamos lo siguiente:

<div aCustomAttribute="value" />

React 15 imprimía un nodo div vacío, es decir, sin atributos, cosa que ya no sucede en esta nueva versión:

// React 16:
<div aCustomAttribute="value" />

La API Portal

Probablemente una de las novedades más llamativas que nos trae React 16: la posibilidad de renderear un hijo dentro de un elemento del DOM que se encuentra fuera de la jerarquía del padre.

Esto, que dicho así suena a trabalenguas, en realidad es mucho más sencillo de usar. Para empezar, es necesario recordar que cuando devolvemos un elemento desde el método render , React monta ese elemento como un hijo del padre más cercano.

class A extends Component {....  render() {
return <div>{ this.props.children}</div>
}
}

Es decir, en este caso el render de A aparecerá dentro del componente padre que lo esté empleando. Sin embargo, hay veces que puede resultar muy útil (por ejemplo, si trabajamos con modales), montar ese elemento en otra ubicación dentro del DOM y es aquí donde los portales entran en juego. Para ello, bastará con realizar lo siguiente:

render() {
// En este caso, React no creará un nodo hijo sino que rendeará
// el elemento dentro del elemento domNode
return ReactDOM.createPortal(
this.props.children,
domNode
);
}

En este caso, el elemento children aparecerá dentro del domNode pasado como segundo argumento, no importa donde se encuentre ubicado. Gracias a esta nueva característica, podríamos tener una clase Modal que se comportase del siguiente modo:

// index.html
<div id="app-root"></div>
<div id="modal-root"></div>
// Modal.jsconst modalRoot = document.getElementById('modal-root');class Modal extends React.Component {
constructor(props) {
super(props);
// Creamos un div que alojará el contenido de la modal para
// poder tener múltiples modales
this.el = document.createElement('div');
}
componentDidMount() {
// Añadimos el div creado en el constructor al elemento modalRoot
modalRoot.appendChild(this.el);
}
componentWillUnmount() {
// Eliminamos el elemento cuando la modal desaparece
modalRoot.removeChild(this.el);
}

render() {
// Usamos la API Portal para renderear ese elemento dentro del div modal-root
return ReactDOM.createPortal(
this.props.children,
this.el,
);
}
}

Evitar un rerender devolviendo null

Ahora gracias a React 16 y aprovechando que la función setState acepta también una función como argumento, podemos evitar rerenders innecesarios devolviendo null en la función pasada a setState . Por ejemplo, el siguiente código NO provocaría un rerender cuando se haya alcanzado 0:

function reduceStock(state, props) {
// Evita el rerender del setState al llegar a 0
if (state.stock === 0) {
return null;
}
return {
stock: state.sotck - 1,
}
}
this.setState(reduceStock);

La API Context

Otra API que viene de la mano de React 16 es la API Context, la cual viene a ser una alternativa a Redux para aquellos que no quieran complicarse mucho la vida, ya que algunas de las cosas que podemos hacer con la famosa librería de Dan Abramov podremos hacerla empleando este API nativa. El funcionamiento es el siguiente.

Lo primero será crear un contexto mediante la llamada:

const {Provider, Consumer} = React.createContext(defaultValue);

La cual como veis devuelve dos objetos: Provider y Consumer . A partir de ahora, cuando React rendeere un contexto Consumer , leerá su valor del Provider más cercano que se encuentre por encima de él. Notad que el valor defaultValue es empleado por un Consumer cuando React no es capaz de encontrar en el árbol un Provider relacionado con él.

Una vez creado el contexto, podremos hacer lo siguiente:

// Creamos el contexto
const Context = createContext()
// El contexto creado proporciona dos propiedades: Provider y Consumer
// sobre las que trabajaremos
const { Provider, Consumer } = Context
// Rendereamos el Provider con algún valor
// Por ejemplo, podemos envolver con este provider
// toda la aplicación
<Provider value={{ name: 'Gerardo' }}>
<App />
</Provider>
// A partir de ahora, dentro de App podemos acceder a los valores anteriores
// de la siguiente forma
<Consumer>
{({ name }) => <span>{`${name}`}</span>}
</Consumer>

Lo único que tenéis que tener en cuenta es que el componente Consumer requiere que como hijo aparezca una función que como argumentos tendrá el valor almacenado en el Provider .

static getDerivedStateFromProps()

Con React 16 el método componentWillReceiveProps pasa a ser declarado como UNSAFE para, a cambio, proponernos una nueva forma de gestionar el estado de nuestro componente en función de las props que recibamos. Pero veamos primero este nuevo método.

El método getDerivedStateFromProps es invocado siempre antes de cada render (a diferencia del método componentWillReceiveProps que era llamado cuando el padre provocaba un nuevo render y no cuando se hacía un setState ). Por tanto, este método se invoca en el render inicial y en los siguientes que nuestra aplicación realice.

Para emplearlo, bastará que lo declaremos en nuestro componente del siguiente modo:

static getDerivedStateFromProps(nextProps, prevState){       
if (nextProps.someValue !== prevState.someValue){
return { someState: nextProps.someValue};
} else return null;
}

Como veis recibe como argumento el valor de las props que serán recibidas y el valor del estado anterior (recordad que es estático y por tanto no tenemos acceso a la variable this ). Puede devolver o bien null , si no es necesario actualizar nada de la propiedad state o bien un objeto con los nuevos valores de dicha propiedad. En el caso de que se produzca un cambio en el estado, el método componentDidUpdate será invocado y podremos realizar las operaciones que normalmente hacíamos en el método componentWillReceiveProps .

Tal y como se dice en la documentación de React, este método solo debería emplearse en raras ocasiones en las que el estado dependa de los cambios en las props a lo largo del tiempo, ya que este tipo de prácticas derivan en un código mucho más verboso y más difícil de entender. Generalmente, muchos de los casos en los que tenderíamos a emplear este nuevo método pueden solucionarse de formas mucho más sencillas. Os recomiendo el artículo de la documentación de React donde lo explican más en detalle ya que es realmente instructivo:

Nueva forma de crear refs

Como ya sabréis, a veces necesitamos emplear referencias a elementos en el DOM en casos como:

  • Gestión de la propiedad focus, selección de texto o reproducción de archivos multimedia.
  • Lanzar animaciones
  • O integrar React con librerías de terceros.

React 16 trae una nueva forma de crear estas referencias mediante el método React.createRef() y de acoplarlas a un elemento mediante el atributo ref . Habitualmente, la invocación al método createRef se realiza en el constructor de modo que la referencia sea accesible en todo el componente:

class MyComponent extends React.Component {
constructor(props) {
super(props);
this.aRef = React.createRef();
}
render() {
return <div ref={this.aRef} />;
}
}

Hecho esto, cuando queramos acceder a dicha referencia en un método de nuestro componente, bastará con que accedamos a la propiedad current del objeto ref creado:

const node = this.aRef.current;

Este valor depende del tipo de nodo al que lo hayamos acoplado:

  • Si lo empleamos como atributo de un elemento HTML, el valor current será el del elemento DOM asociado
  • En el caso de que lo hayamos usado en un componente, current recibirá la instancia de dicho componente ya montada

Notad que puesto que los componentes funcionales no tienen instancia no podemos añadirles un ref.

Y con esto termino el repaso de las principales novedades de React 16. Espero no haberme dejado ninguna y si lo he hecho dejadla en los comentarios para que pueda ir completando el artículo.

¿Quieres recibir más artículos como este?

Suscríbete a nuestra newsletter:

Written by

Entre paseo y paseo con Simba desarrollo en Symfony y React

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store