Hace ya un tiempo muchas aplicaciones empezaron a sacar una funcionalidad llamada "darkmode", la cual cambia el tema de la web o aplicación a fondos oscuros y letras claras.

Por ejemplo así se ve Twitter en "lightmode"

Y así se ve Twitter con "darkmode"

En este post vamos a aprender como agregarle darkmode a tu web usando React y API Context. A la hora de explicar conceptos vamos a asumir que tienen conocimientos sobre cómo funciona React (componentes y ciclo de vida de los mismos).

Arranquemos entendiendo el concepto de API Context y para que sirve.

API Context nos provee una forma de pasar props de forma descendente en un árbol de componentes sin necesidad de hacerlo manualmente en cada paso

Esto lo convierte en la herramienta ideal para esta funcionalidad ya que vamos a poder definir nuestro theme en nuestro componente de más alto nivel y después poder consumir esa información fácilmente a lo largo de toda nuestra aplicación.

Context necesita de tres etapas para funcionar adecuadamente.

  1. Creación de contexto
  2. Crear el provider en el componente padre para pasar la información a los hijos
  3. Consumir el valor desde los hijos

  1. Crear el contexto es realmente fácil y se hace agregando la siguiente linea al principio de nuestro componente donde vamos a tener la información a compartir
const ThemeContext = React.createContext('light')

2. En ese mismo componente vamos a crear y asignar el provider de la siguiente forma:

class App extends React.Component {
  render() {
    return (
      <ThemeContext.Provider value="Valor de prueba">
        <First />
      </ThemeContext.Provider>
    )
  }
}

3. Para consumir esta información en algún componente hijo, tenemos que hacer lo siguiente:

class ChildComponent extends React.Component {
  static contextType = ThemeContext;

  render() {
    return (
      <p>Valor Context: <strong>{this.context}</strong></p>
    )
  }
}

Y si mezclamos esos tres pasos en un solo proyecto, nos queda lo siguiente:

Bien, ahora que tenemos las nociones básicas de como funciona context, lo que vamos a hacer es darle la capacidad a un botón de realizar ese cambio entre el light y el dark mode.

Para eso vamos a crear 3 componentes.

  1. App: Va a hacer nuestro componente padre
  2. Button: Va a tener la responsabilidad de cambiar el theme
  3. Value: Va a mostrar el valor del theme actual

App

const ThemeContext = React.createContext('light') //Creo contexto

class App extends React.Component {
  constructor(props) {
    super(props)
    
    this.state = {
      theme: 'dark' //Setteo un theme por defecto
    }
  }
  
  //Callback para recibir el click del boton
  changeTheme() {
    const actualTheme = this.state.theme
    let newTheme
    if (actualTheme == 'dark') {
      newTheme = 'light'
    } else {
      newTheme = 'dark'
    }
    
    this.setState({
      theme: newTheme
    })
  }
  
  render() {
    const { theme } = this.state
    return (
      <ThemeContext.Provider value={theme}>
        <Button changeTheme={() => this.changeTheme()}/>
        <Value />
      </ThemeContext.Provider>
    );
  }
}

Button

class Button extends React.Component {
  render() {
    return (
      <button 
        onClick={() => this.props.changeTheme()} 
        >
        Cambiar theme
      </button>
    )
  }
}

Value

class Value extends React.Component {
  static contextType = ThemeContext;
  render() {
    return(
      <div className="testContainer">
        <p>Theme actual: <strong>{this.context}</strong></p>
      </div>
    )
  }
}

Si todo salió bien, deberíamos tener un resultado como el siguiente:

Una vez que logramos este resultado, lo único que nos queda por hacer es asignar nuestro valor a los distintos componentes para poder darles los estilos deseados. Por ejemplo si queremos que nuestro botón cambie su color de fondo dependiendo el theme, podemos hacer lo siguiente:

Button

Javascript

class Button extends React.Component {  
  static contextType = ThemeContext;
  render() {
    return (
      <button 
        onClick={() => this.props.changeTheme()} 
        className={this.context}
        >
        Cambiar theme
      </button>
    )
  }
}

CSS

.dark {
  background-color: #000;
  color: #FFF;
}

Listo! Ahora solo es cuestión de aplicar un poco de imaginación para darle estilos a todos nuestros componentes según el tema seleccionado y lograr cosas como estas:

Si llegaste hasta acá y pudiste aplicar el darkmode a algún ejemplo, te pido que me lo compartas en Twitter. Si algo no te funcionó o te quedó alguna duda de uno de los pasos, mis mensajes directos están abiertos y con gusto voy a tratar de ayudarte!