React, una de las librerías más conocidas para crear aplicaciones basadas en componentes con JavaScript, lanzó hace unas semanas una actualización destacable que venimos a comentarte.

La API de context ya es oficial

La API de contexto de React nos sirve para establecer propiedades globales en un objeto padre sin tener que pasárselas a cada una de sus elementos hijos. Hace bastante tiempo que tenemos esta función en React, sin embargo, estaba en fase beta y eran muy comunes los errores.

Un ejemplo claro, que incluso podemos ver en la documentación de React es la creación de temas.

Para enseñarte un caso de uso diferente, te dejo una pieza de código en la que pasamos un objeto de Usuario a todos los componentes.

Se me ocurre que esto tendría sentido por ejemplo en una app de banca, en la que necesitaremos para todas las peticiones al objeto usuario. Para esto nos ayuda el contexto y principalmente para no tener que estar pasando propiedades de padres a hijos

¿Cómo funciona?

  1. Necesitamos crear un contexto, al que podremos añadirle por defecto un valor:
const clientContext = React.createContext()
  1. Si no introdujimos un valor por defecto, o deseamos cambiarlo deberemos de hacerlo instanciando al componente Provider que tiene nuestra referencia:
<clientContext.Provider value={userInfo}>
    <h3>Bienvenido, {userInfo.name}</h3>
    <AvailableCredits />
</clientContext.Provider>
  1. Una vez querramos acceder a esta información, sin importar la longitud de la cadena de hijos, usaremos el componente Consumer que tiene nuestra referencia de contexto:
class AvailableCredits extends React.Component {
  render() {
    return (
      <clientContext.Consumer>
        {user => <ListCredits credits={user.preAdmitedCredits} />}
      </clientContext.Consumer>
    )
  }
}

class ListCredits extends React.Component {
  render() {
    return(
      <div className="Credits">
        <h3>Mis creditos preconcedidos</h3>
        {
          this.props.credits.forEach(credit => {
            return (
              <span>hi</span>
            )
          })
        }
      </div>
    )
  }
}

Al completo, quedaría así:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

let userInfo = {
  name: 'OpenWeby',
  id: 34,
  products: 3,
  type: 'person',
  nif: '11111111E',
  preAdmitedCredits: [{
    currency: 'EUR',
    quant: 3454.33
  }]
}

const clientContext = React.createContext()

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Kontext Bank</h1>
        </header>
        <clientContext.Provider value={userInfo}>
          <h3>Bienvenido, {userInfo.name}</h3>
          <AvailableCredits />
        </clientContext.Provider>
      </div>
    );
  }
}

class AvailableCredits extends React.Component {
  render() {
    return (
      <clientContext.Consumer>
        {user => <ListCredits credits={user.preAdmitedCredits} />}
      </clientContext.Consumer>
    )
  }
}

class ListCredits extends React.Component {
  render() {
    return(
      <div className="Credits">
        <h3>Mis creditos preconcedidos</h3>
        {
          this.props.credits.forEach(credit => {
            return (
              <span>hi</span>
            )
          })
        }
      </div>
    )
  }
}

export default App;

Cambios en la referencia para los formularios

createRef: Una nueva manera de gestionar las referencias

En esta versión se ha estandarizado la manera de crear referencia para los inputs de los formularios. Una referencia nos sirve en este caso para poder acceder al elemento.

E.g.: Para poder hacerle focus al input una vez pasen 3s desde la carga de nuestra aplicación:

class MyAwesomeComponent extends React.Component {
  constructor(props)
  {
    super(props)
    this.awesomeRef = React.createRef()
  }
  componentDidMount() {
    window.setTimeout(() => {
      this.awesomeRef.current.focus()
    }, 3000)
  }
  render() {
    return(
      <div className="panel">
        <input type="text" ref={this.awesomeRef} /> 
      </div>
    )
  }
}

forwardRef: Usa la referencia en más de un elemento

Si queremos cambiar la estructura de nuestra aplicación y, por ejemplo, meter todos los inputs dentro de un nuevo componente, tendremos que pasar la referencia de alguna manera. El método forwardRef nos ayudará a ello:

class MyAwesomeComponent extends React.Component {
  constructor(props)
  {
    super(props)
    this.awesomeRef = React.createRef()
  }
  componentDidMount() {
    window.setTimeout(() => {
      this.awesomeRef.current.focus()
    }, 3000)
  }
  render() {
    return(
      <div className="panel">
        <allForm ref={this.awesomeRef} />
      </div>
    )
  }
}

let allForm = React.forwardRef((props, ref) => (
  <input type="text" ref={this.awesomeRef} />
))

Cambios en los métodos del ciclo de vida

getDerivedStateFromProps()

Este método se instanciará cuando un componente es inicializado o es modificado de alguna manera. Con los parámetros nextProps y prevProps(en ese order) podremos verificar si las props se han visto modificadas.

No siempre el hecho de que un componente se actualize tiene que ver con modificar sus props, por tanto si queremos cambiar algo sólo cuando las props cambien, deberemos de verificar dentro de el método que haya habido un cambio.

class DerivedComponent extends React.Component {
  static getDerivedStateFromProps(prev, next) {
    let changed
    if(prev != next)
    {
      changed = true
    } else {
      changed = false
    }
    console.log(changed)
  }
  render() {
    return (
      <div>
        <h1> Pretty Component </h1>
      </div>
    )
  }
}

getSnapshotBeforeUpdate()

Con este método, podremos tener acceso antes de que un componente cambie tanto a las props como al state que había en ese momento, ya que, esta actualización podría derivar que las mencionadas propiedades cambien.

En este método no podremos hacer nada más que retornar algo pues la lógica que necesitemos implementar lo haremos en el método componentDidUpdate(su tercer parámetro, snapshot, devuelve lo que hayamos retornado en este método).

Podríamos usar este método para tener siempre una copia de las props y el state anterior. Sería algo tal que así:

class SnapshotComponent extends React.Component {
  constructor(props) {
    super(props)

    this.prev = {
      props: {},
      state: {}
    }
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
    return { prevProps, prevState }
  }
  componentDidUpdate(prevProps, prevState, snapshot) {
    this.prev = { props: snapshot.prevProps, state: snapshot.prevState }
  }
  componentDidMount() {
    this.forceUpdate()
  }
  render() {
    return (
      <div>
        <h1> Pretty Component </h1>
      </div>
    );
  }
}

Conclusiones

Como hemos visto a lo largo de este artículo React se está centrando en estandarizar algunas partes de su código que se encontraban en fases de pruebas o que no estaban funcionando como deberían.

En mi opinión, seguiremos viendo en las próximas versiones como React sigue haciendo más intuitivo y estable el uso de su librería.