Implementación de Redux en React usando RxJS

Al explorar las características de inyección de dependencias en servicios de Angular y el uso de flujos observables en RxJS, decidí aplicar estas ideas a un proyecto de React.

Para comenzar, necesitamos:

  • Un proyecto de React vacío.
  • Instalar RxJS, ejecutando npm install rxjs --save.

Enfoque de Implementación

  1. Según los requisitos del negocio, encapsular los estados globales necesarios en diferentes clases de servicio para lograr una separación de datos.
  2. Crear un proveedor personalizado y un método de conexión para inyectar los estados globales y servicios en los componentes.
  3. Escribir mapStateToProps en los componentes para especificar qué servicios y estados globales se desean obtener.

Código Ejemplo

// Envolver la clase de servicio para crear una instancia única
const encapsularServicio = (Servicio, datosIniciales = {}) => {
  const servicio = new Servicio(datosIniciales);
  return () => servicio;
}

// Clase de servicio
import { Subject } from 'rxjs';
import { encapsularServicio } from 'Utils/servicios';

class ServicioPrueba {
  constructor(datosIniciales = {}) {
    this.datos = datosIniciales;
  }

  datos$ = new Subject();

  suscribirse = (callback) => {
    this.suscripcion = this.datos$.subscribe(callback);
    this.datos$.next(this.datos);

    return () => {
      this.suscripcion.unsubscribe();
    }
  }

  agregar = (datos) => {
    this.datos.contador = Number(datos.contador) + Number(this.datos.contador);
    this.datos$.next(this.datos);
  }

  establecer = (datos) => {
    this.datos = { ...this.datos, ...datos };
    this.datos$.next(this.datos);
  }
}

const datosIniciales = {
  contador: 0,
  prueba1: 'Hola',
  prueba2: 'Mundo'
};

export default encapsularServicio(ServicioPrueba, datosIniciales)();

// Obtener un contexto único
const obtenerContexto = (() => {
  const Contexto = React.createContext(null);
  Contexto.displayName = 'proveedorDeServicios';

  return () => Contexto;
})();

// Inyectar todos los estados globales por defecto
const mapStateToPropsPorDefecto = (datos) => ({
  ...datos
});

/**
 * Componente de orden superior para inyectar servicios
 */
function ProveedorDeServicios({ servicios = {}, hijos = null }) {
  const Contexto = obtenerContexto();
  const [valorContexto, actualizarValorContexto] = useState({
    ...servicios
  });
  const listaDesuscripciones = [];

  useEffect(() => {
    Object.keys(servicios).forEach((clave) => {
      const desuscribirse = servicios[clave].suscribirse(_datos => {
        actualizarValorContexto({
          ...valorContexto,
          ..._datos
        });
      });
      listaDesuscripciones.push(desuscribirse);
    });

    return () => {
      listaDesuscripciones.forEach(desuscribirse => {
        typeof desuscribirse === 'function' && desuscribirse();
      });
    }
  }, []);

  return (
    <Contexto.Provider valor={valorContexto}>{hijos}</Contexto.Provider>
  );
}

// Inyectar estados globales y objetos de servicio en los componentes
const inyectar = (mapStateToProps = mapStateToPropsPorDefecto) => {
  const Contexto = obtenerContexto();

  return (Componente) => (props) => (
    <Contexto.Consumer>
      {datos => {
        const nuevasProps = mapStateToProps(datos);
        return <Componente {...props} {...nuevasProps} />
      }}
    </Contexto.Consumer>
  );
}

// Uso: envolver el componente principal con ProveedorDeServicios
ReactDOM.render(
  <ProveedorDeServicios servicios={{ servicioPrueba: servicioPrueba }}>
    <Indice />
  </ProveedorDeServicios>,
  document.getElementById("root")
);

// En los componentes necesarios, usar el método inyectar
const mapStateToProps = (datos) => {
  const {
    contador,
    servicioPrueba
  } = datos;

  return {
    contador,
    servicioPrueba
  };
}

export default inyectar(mapStateToProps)(Suscriptor);

// Ahora el componente Suscriptor tiene acceso a los estados globales contador y al servicio servicioPrueba a través de las props.

Este enfoque permite una mejor comprensión de los componentes de orden superior y el uso de Context en React, simplificando el uso en comparación con Redux tradicional al combinar mapStateToProps y mapDispatchToProps. Se ha empaquetado esta funcionalidad como un módulo npm bajo el nombre service-redux, disponible en este enlace.

Etiquetas: React RxJS state-management high-order-components

Publicado el 8-30 00:33