Tener servicios en la aplicación React

201
Dennis Nerush 2016-03-08 12:53.

Vengo del mundo angular donde podría extraer la lógica a un servicio / fábrica y consumirlos en mis controladores.

Estoy tratando de entender cómo puedo lograr lo mismo en una aplicación React.

Digamos que tengo un componente que valida la entrada de la contraseña del usuario (su fuerza). Su lógica es bastante compleja, por lo tanto, no quiero escribirla en el componente en sí.

¿Dónde debo escribir esta lógica? ¿En una tienda si estoy usando flux? ¿O hay una mejor opción?

11 answers

66
aphenine 2017-07-15 08:30.

La primera respuesta no refleja el paradigma actual de Container vs Presenter .

Si necesita hacer algo, como validar una contraseña, es probable que tenga una función que lo haga. Estaría pasando esa función a su vista reutilizable como accesorio.

Contenedores

Entonces, la forma correcta de hacerlo es escribir un ValidatorContainer, que tendrá esa función como una propiedad, y envolver el formulario en él, pasando los accesorios correctos al niño. Cuando se trata de su vista, su contenedor validador envuelve su vista y la vista consume la lógica de los contenedores.

La validación puede realizarse en las propiedades del contenedor, pero si está utilizando un validador de terceros o cualquier servicio de validación simple, puede usar el servicio como una propiedad del componente del contenedor y usarlo en los métodos del contenedor. He hecho esto para componentes relajantes y funciona muy bien.

Proveedores

Si se necesita un poco más de configuración, puede utilizar un modelo de proveedor / consumidor. Un proveedor es un componente de alto nivel que se envuelve en algún lugar cerca y debajo del objeto de la aplicación superior (el que usted monta) y proporciona una parte de sí mismo, o una propiedad configurada en la capa superior, a la API de contexto. Luego configuro los elementos de mi contenedor para consumir el contexto.

Las relaciones de contexto padre / hijo no tienen que estar cercanas entre sí, solo el hijo tiene que descender de alguna manera. Las tiendas Redux y el React Router funcionan de esta manera. Lo he usado para proporcionar un contexto de descanso raíz para mis contenedores de descanso (si no proporciono el mío).

(nota: la API de contexto está marcada como experimental en los documentos, pero no creo que lo sea más, considerando qué la está usando).

//An example of a Provider component, takes a preconfigured restful.js
//object and makes it available anywhere in the application
export default class RestfulProvider extends React.Component {
	constructor(props){
		super(props);

		if(!("restful" in props)){
			throw Error("Restful service must be provided");
		}
	}

	getChildContext(){
		return {
			api: this.props.restful
		};
	}

	render() {
		return this.props.children;
	}
}

RestfulProvider.childContextTypes = {
	api: React.PropTypes.object
};

Middleware

Otra forma que no he probado, pero que he visto utilizada, es usar middleware junto con Redux. Usted define su objeto de servicio fuera de la aplicación, o al menos, más alto que la tienda redux. Durante la creación de la tienda, inyecta el servicio en el middleware y el middleware maneja cualquier acción que afecte al servicio.

De esta manera, podría inyectar mi objeto restful.js en el middleware y reemplazar mis métodos de contenedor con acciones independientes. Todavía necesitaría un componente contenedor para proporcionar las acciones a la capa de vista de formulario, pero connect () y mapDispatchToProps me cubren allí.

El nuevo react-router-redux v4 usa este método para impactar el estado del historial, por ejemplo.

//Example middleware from react-router-redux
//History is our service here and actions change it.

import { CALL_HISTORY_METHOD } from './actions'

/**
 * This middleware captures CALL_HISTORY_METHOD actions to redirect to the
 * provided history object. This will prevent these actions from reaching your
 * reducer or any middleware that comes after this one.
 */
export default function routerMiddleware(history) {
  return () => next => action => {
    if (action.type !== CALL_HISTORY_METHOD) {
      return next(action)
    }

    const { payload: { method, args } } = action
    history[method](...args)
  }
}

128
Wojtek Majerski 2018-06-22 13:30.

El problema se vuelve extremadamente simple cuando te das cuenta de que un servicio Angular es solo un objeto que ofrece un conjunto de métodos independientes del contexto. Es solo el mecanismo Angular DI lo que lo hace parecer más complicado. La DI es útil ya que se encarga de crear y mantener instancias por usted, pero realmente no la necesita.

Considere una biblioteca AJAX popular llamada axios (de la que probablemente haya oído hablar):

import axios from "axios";
axios.post(...);

¿No se comporta como un servicio? Proporciona un conjunto de métodos responsables de cierta lógica específica y es independiente del código principal.

Su caso de ejemplo fue sobre la creación de un conjunto aislado de métodos para validar sus entradas (por ejemplo, verificar la seguridad de la contraseña). Algunos sugirieron poner estos métodos dentro de los componentes, lo que para mí es claramente un anti-patrón. ¿Qué sucede si la validación implica realizar y procesar llamadas de backend XHR o realizar cálculos complejos? ¿Combinaría esta lógica con los controladores de clic del mouse y otras cosas específicas de la interfaz de usuario? Disparates. Lo mismo con el enfoque contenedor / HOC. ¿Envolver su componente solo para agregar un método que verificará si el valor tiene un dígito? Venga.

Simplemente crearía un nuevo archivo llamado "ValidationService.js" y lo organizaría de la siguiente manera:

const ValidationService = {
    firstValidationMethod: function(value) {
        //inspect the value
    },

    secondValidationMethod: function(value) {
        //inspect the value
    }
};

export default ValidationService;

Luego, en su componente:

import ValidationService from "./services/ValidationService.js";

...

//inside the component
yourInputChangeHandler(event) {

    if(!ValidationService.firstValidationMethod(event.target.value) {
        //show a validation warning
        return false;
    }
    //proceed
}

Utilice este servicio desde cualquier lugar que desee. Si las reglas de validación cambian, debe concentrarse solo en el archivo ValidationService.js.

Es posible que necesite un servicio más complicado que depende de otros servicios. En este caso, su archivo de servicio puede devolver un constructor de clase en lugar de un objeto estático para que pueda crear una instancia del objeto usted mismo en el componente. También puede considerar implementar un singleton simple para asegurarse de que siempre haya solo una instancia del objeto de servicio en uso en toda la aplicación.

40
Kildareflare 2017-11-09 12:35.

Necesitaba algo de lógica de formato para compartir entre múltiples componentes y, como desarrollador de Angular, también me inclinaba naturalmente hacia un servicio.

Compartí la lógica poniéndola en un archivo separado

function format(input) {
    //convert input to output
    return output;
}

module.exports = {
    format: format
};

y luego lo importó como un módulo

import formatter from '../services/formatter.service';

//then in component

    render() {

        return formatter.format(this.props.data);
    }
32
Jake Roby 2016-03-08 18:24.

Tenga en cuenta que el propósito de React es acoplar mejor las cosas que lógicamente deberían estar acopladas. Si está diseñando un método complicado de "validar contraseña", ¿dónde debería acoplarse?

Bueno, necesitará usarlo cada vez que el usuario necesite ingresar una nueva contraseña. Esto podría ser en la pantalla de registro, una pantalla de "contraseña olvidada", una pantalla de administrador "restablecer contraseña para otro usuario", etc.

Pero en cualquiera de esos casos, siempre estará vinculado a algún campo de entrada de texto. Entonces ahí es donde debería acoplarse.

Cree un componente React muy pequeño que consista únicamente en un campo de entrada y la lógica de validación asociada. Ingrese ese componente dentro de todos los formularios que puedan querer tener una contraseña.

Es esencialmente el mismo resultado que tener un servicio / fábrica para la lógica, pero lo está acoplando directamente a la entrada. Por lo tanto, ahora nunca necesita decirle a esa función dónde buscar su entrada de validación, ya que está unida permanentemente.

13
Juraj 2018-01-17 15:26.

También vine del área de Angular.js y los servicios y fábricas en React.js son más simples.

Puede usar funciones o clases simples, estilo de devolución de llamada y evento Mobx como yo :)

// Here we have Service class > dont forget that in JS class is Function
class HttpService {
  constructor() {
    this.data = "Hello data from HttpService";
    this.getData = this.getData.bind(this);
  }

  getData() {
    return this.data;
  }
}


// Making Instance of class > it's object now
const http = new HttpService();


// Here is React Class extended By React
class ReactApp extends React.Component {
  state = {
    data: ""
  };

  componentDidMount() {
    const data = http.getData();

    this.setState({
      data: data
    });
  }

  render() {
    return <div>{this.state.data}</div>;
  }
}

ReactDOM.render(<ReactApp />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  
  <div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

</body>
</html>

Aquí hay un ejemplo simple:

12
corolla 2017-10-13 04:37.

Misma situación: después de haber realizado múltiples proyectos de Angular y pasar a React, no tener una forma simple de proporcionar servicios a través de DI parece una pieza faltante (aparte de los detalles del servicio).

Usando decoradores de contexto y ES7 podemos acercarnos:

https://jaysoo.ca/2015/06/09/react-contexts-and-dependency-injection/

Parece que estos chicos han dado un paso más / en una dirección diferente:

http://blog.wolksoftware.com/dependency-injection-in-react-powered-inversifyjs

Todavía se siente como trabajar contra la corriente. Revisaré esta respuesta en 6 meses después de emprender un proyecto React importante.

EDITAR: Vuelve 6 meses después con un poco más de experiencia React. Considere la naturaleza de la lógica:

  1. ¿Está vinculado (solo) a la interfaz de usuario? Moverlo a un componente (respuesta aceptada).
  2. ¿Está vinculado (solo) a la gestión estatal? Moverlo a un thunk .
  3. ¿Atado a ambos? Mover a un archivo separado, consumir en componente a través de un selector y en thunks.

Algunos también recurren a los HOC para su reutilización, pero para mí lo anterior cubre casi todos los casos de uso. Además, considere la posibilidad de escalar la administración del estado utilizando patos para mantener las preocupaciones separadas y centradas en la IU del estado.

7
bob 2017-12-10 09:12.

Yo también soy de Angular y estoy probando React, a partir de ahora, una forma recomendada (?) Parece ser usar componentes de alto orden :

Un componente de orden superior (HOC) es una técnica avanzada en React para reutilizar la lógica del componente. Los HOC no son parte de la API de React, per se. Son un patrón que surge de la naturaleza compositiva de React.

Digamos que tiene inputy le textareagusta aplicar la misma lógica de validación:

const Input = (props) => (
  <input type="text"
    style={props.style}
    onChange={props.onChange} />
)
const TextArea = (props) => (
  <textarea rows="3"
    style={props.style}
    onChange={props.onChange} >
  </textarea>
)

Luego escriba un HOC que valide y diseñe el componente envuelto:

function withValidator(WrappedComponent) {
  return class extends React.Component {
    constructor(props) {
      super(props)

      this.validateAndStyle = this.validateAndStyle.bind(this)
      this.state = {
        style: {}
      }
    }

    validateAndStyle(e) {
      const value = e.target.value
      const valid = value && value.length > 3 // shared logic here
      const style = valid ? {} : { border: '2px solid red' }
      console.log(value, valid)
      this.setState({
        style: style
      })
    }

    render() {
      return <WrappedComponent
        onChange={this.validateAndStyle}
        style={this.state.style}
        {...this.props} />
    }
  }
}

Ahora esos HOC comparten el mismo comportamiento de validación:

const InputWithValidator = withValidator(Input)
const TextAreaWithValidator = withValidator(TextArea)

render((
  <div>
    <InputWithValidator />
    <TextAreaWithValidator />
  </div>
), document.getElementById('root'));

Creé una demostración simple .

Editar : otra demostración está usando accesorios para pasar una serie de funciones para que pueda compartir la lógica compuesta por múltiples funciones de validación a través de HOCs como:

<InputWithValidator validators={[validator1,validator2]} />
<TextAreaWithValidator validators={[validator1,validator2]} />

Edit2 : React 16.8+ proporciona una nueva función, Hook , otra buena forma de compartir la lógica.

const Input = (props) => {
  const inputValidation = useInputValidation()

  return (
    <input type="text"
    {...inputValidation} />
  )
}

function useInputValidation() {
  const [value, setValue] = useState('')
  const [style, setStyle] = useState({})

  function handleChange(e) {
    const value = e.target.value
    setValue(value)
    const valid = value && value.length > 3 // shared logic here
    const style = valid ? {} : { border: '2px solid red' }
    console.log(value, valid)
    setStyle(style)
  }

  return {
    value,
    style,
    onChange: handleChange
  }
}

https://stackblitz.com/edit/react-shared-validation-logic-using-hook?file=index.js

4
Alireza 2018-05-12 18:51.

El servicio no se limita a Angular, incluso en Angular2 + ,

El servicio es solo una colección de funciones auxiliares ...

Y hay muchas formas de crearlos y reutilizarlos en la aplicación ...

1) Pueden ser todas funciones separadas que se exportan desde un archivo js, ​​similar a la siguiente:

export const firstFunction = () => {
   return "firstFunction";
}

export const secondFunction = () => {
   return "secondFunction";
}
//etc

2) También podemos usar el método de fábrica como, con una colección de funciones ... con ES6 puede ser una clase en lugar de un constructor de funciones:

class myService {

  constructor() {
    this._data = null;
  }

  setMyService(data) {
    this._data = data;
  }

  getMyService() {
    return this._data;
  }

}

En este caso, necesita crear una instancia con una nueva clave ...

const myServiceInstance = new myService();

Además, en este caso, cada instancia tiene su propia vida, así que tenga cuidado si desea compartirla, en ese caso, debe exportar solo la instancia que desee ...

3) Si su función y utilidades no se van a compartir, incluso puede ponerlas en el componente React, en este caso, como función en su componente react ...

class Greeting extends React.Component {
  getName() {
    return "Alireza Dezfoolian";
  }

  render() {
    return <h1>Hello, {this.getName()}</h1>;
  }
}

4) Otra forma en que puede manejar las cosas, podría ser usando Redux , es una tienda temporal para usted, por lo que si la tiene en su aplicación React , puede ayudarlo con muchas funciones de getter setter que usa ... Es como una gran tienda que realizan un seguimiento de sus estados y pueden compartirlo entre sus componentes, por lo que pueden deshacerse de muchas molestias para los elementos de getter setter que usamos en los servicios ...

Siempre es bueno hacer un código DRY y no repetir lo que se debe usar para que el código sea reutilizable y legible, pero no intente seguir las formas angulares en la aplicación React , como se menciona en el elemento 4, el uso de Redux puede reducir su necesidad de servicios y limita su uso para algunas funciones auxiliares reutilizables como el elemento 1 ...

1
sibidiba 2016-09-13 19:03.

Estoy en la misma bota que tú. En el caso que mencionas, implementaría el componente de IU de validación de entrada como un componente de React.

Estoy de acuerdo en que la implementación de la lógica de validación en sí no debería (no debe) estar acoplada. Por lo tanto, lo pondría en un módulo JS separado.

Es decir, para la lógica que no debe acoplarse, use un módulo / clase JS en un archivo separado y use require / import para desacoplar el componente del "servicio".

Esto permite la inyección de dependencia y la prueba unitaria de los dos de forma independiente.

1
Juraj 2018-01-17 15:31.

o puede inyectar la herencia de clase "http" en React Component

a través del objeto de utilería.

  1. actualización:

    ReactDOM.render(<ReactApp data={app} />, document.getElementById('root'));
    
  2. Simplemente edite React Component ReactApp de esta manera:

    class ReactApp extends React.Component {
    
    state = {
    
        data: ''
    
    }
    
        render(){
    
        return (
            <div>
            {this.props.data.getData()}      
            </div>
    
        )
        }
    }
    
0
Muhammad Shahryar 2020-05-27 05:34.

Bueno, el patrón más utilizado para la lógica reutilizable con el que me he encontrado es escribir un gancho o crear un archivo utils. Depende de lo que quieras lograr.

hooks/useForm.js

Por ejemplo, si desea validar los datos del formulario, crearía un gancho personalizado llamado useForm.js y le proporcionaría los datos del formulario y, a cambio, me devolvería un objeto que contiene dos cosas:

Object: {
    value,
    error,
}

Definitivamente puede devolver más cosas a medida que avanza.

utils/URL.js

Otro ejemplo sería como si quisiera extraer información de una URL, luego crearía un archivo de utilidades que contenga una función y lo importaría donde sea necesario:

 export function getURLParam(p) {
...
}

MORE COOL STUFF

La estrella de HGTV, Christina Hall, revela que tiene 'envenenamiento por mercurio y plomo' probablemente por voltear 'casas asquerosas'

La estrella de HGTV, Christina Hall, revela que tiene 'envenenamiento por mercurio y plomo' probablemente por voltear 'casas asquerosas'

La estrella de HGTV, Christina Hall, revela que le diagnosticaron envenenamiento por mercurio y plomo, probablemente debido a su trabajo como manipuladora de casas.

La estrella de 'Love Is Blind' Brennon Lemieux responde a los cargos de violencia doméstica

La estrella de 'Love Is Blind' Brennon Lemieux responde a los cargos de violencia doméstica

Recientemente salió a la luz un informe policial que acusa a la estrella de 'Love Is Blind', Brennon, de violencia doméstica. Ahora, Brennon ha respondido a los reclamos.

Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia Judd en un momento festivo de pánico

Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia Judd en un momento festivo de pánico

Conozca cómo Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia mientras organizaba la primera celebración de Acción de Gracias desde que murió su madre, Naomi Judd.

Experto en lenguaje corporal explica los 'paralelos' entre Kate Middleton y la princesa Diana

Experto en lenguaje corporal explica los 'paralelos' entre Kate Middleton y la princesa Diana

Descubra por qué un destacado experto en lenguaje corporal cree que es fácil trazar "tales paralelismos" entre la princesa Kate Middleton y la princesa Diana.

Los láseres arrojan luz sobre por qué necesita cerrar la tapa antes de descargar

Los láseres arrojan luz sobre por qué necesita cerrar la tapa antes de descargar

Los inodoros arrojan columnas de aerosol invisibles con cada descarga. ¿Como sabemos? La prueba fue capturada por láseres de alta potencia.

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

¿Caduca el repelente de insectos?

¿Caduca el repelente de insectos?

¿Sigue siendo efectivo ese lote de repelente de insectos que te quedó del verano pasado? Si es así, ¿por cuánto tiempo?

Actualice a un Sonicare por tan solo $ 30

Actualice a un Sonicare por tan solo $ 30

¿Quiere probar un cepillo de dientes Sonicare sin gastar mucho dinero en uno de sus modelos favoritos de gama alta? Puede comprar un kit de la Serie 2 o Serie 3 por tan solo $ 30 hoy en Amazon. Haga clic aquí para ver la lista completa de modelos elegibles y tenga en cuenta que se descontarán $ 10 adicionales en su carrito.

Ponle una tapa. En realidad, ponle una tapa a todo. Consigue 12 tapas de cocina elásticas de silicona por $14. [Exclusivo]

Ponle una tapa. En realidad, ponle una tapa a todo. Consigue 12 tapas de cocina elásticas de silicona por $14. [Exclusivo]

Tapas elásticas de silicona de Tomorrow's Kitchen, paquete de 12 | $14 | Amazonas | Código promocional 20OFFKINJALids son básicamente los calcetines de la cocina; siempre perdiéndose, dejando contenedores huérfanos que nunca podrán volver a cerrarse. Pero, ¿y si sus tapas pudieran estirarse y adaptarse a todos los recipientes, ollas, sartenes e incluso frutas en rodajas grandes que sobran? Nunca más tendrás que preocuparte por perder esa tapa tan específica.

Cuéntanos tus mejores trucos de Washington, DC

Cuéntanos tus mejores trucos de Washington, DC

Hemos pirateado algunas ciudades industriales en esta columna, como Los Ángeles y Las Vegas. Ahora es el momento de una ciudad militar-industrial-compleja.

Un minorista está eliminando su sección de tallas grandes y mezclando tallas más grandes con todo lo demás

Un minorista está eliminando su sección de tallas grandes y mezclando tallas más grandes con todo lo demás

Un minorista está enlatando su sección de tallas grandes. Pero no están tomando la categoría solo en línea o descontinuándola por completo.

Patinaje artístico de EE. UU. 'frustrado' por falta de decisión final en evento por equipos, pide una decisión justa

Patinaje artístico de EE. UU. 'frustrado' por falta de decisión final en evento por equipos, pide una decisión justa

El equipo está a la espera de las medallas que ganó en los Juegos Olímpicos de Invierno de 2022 en Beijing, ya que se está resolviendo un caso de dopaje que involucra a la patinadora artística rusa Kamila Valieva.

Los compradores de Amazon dicen que duermen 'como un bebé mimado' gracias a estas fundas de almohada de seda que cuestan tan solo $ 10

Los compradores de Amazon dicen que duermen 'como un bebé mimado' gracias a estas fundas de almohada de seda que cuestan tan solo $ 10

Miles de compradores de Amazon recomiendan la funda de almohada de seda Mulberry, y está a la venta en este momento. La funda de almohada de seda viene en varios colores y ayuda a mantener el cabello suave y la piel clara. Compre las fundas de almohada de seda mientras tienen hasta un 46 por ciento de descuento en Amazon

Se busca al corredor de los Bengals Joe Mixon por orden de arresto emitida por presuntamente apuntar con un arma de fuego a una mujer

Se busca al corredor de los Bengals Joe Mixon por orden de arresto emitida por presuntamente apuntar con un arma de fuego a una mujer

El jueves se presentó una denuncia de delito menor amenazante agravado contra Joe Mixon.

Profesor de la Universidad de Purdue arrestado por presuntamente traficar metanfetamina y proponer favores sexuales a mujeres

Profesor de la Universidad de Purdue arrestado por presuntamente traficar metanfetamina y proponer favores sexuales a mujeres

El Departamento de Policía de Lafayette comenzó a investigar a un profesor de la Universidad de Purdue en diciembre después de recibir varias denuncias de un "hombre sospechoso que se acercaba a una mujer".

Concept Drift: el mundo está cambiando demasiado rápido para la IA

Concept Drift: el mundo está cambiando demasiado rápido para la IA

Al igual que el mundo que nos rodea, el lenguaje siempre está cambiando. Mientras que en eras anteriores los cambios en el idioma ocurrían durante años o incluso décadas, ahora pueden ocurrir en cuestión de días o incluso horas.

India me está pateando el culo

India me está pateando el culo

Estoy de vuelta por primera vez en seis años. No puedo decirte cuánto tiempo he estado esperando esto.

ℝ

“And a river went out of Eden to water the garden, and from thence it was parted and became into four heads” Genesis 2:10. ? The heart is located in the middle of the thoracic cavity, pointing eastward.

¿Merrick Garland le ha fallado a Estados Unidos?

Es más de la mitad de la presidencia de Biden. ¿Qué está esperando Merrick Garland?

¿Merrick Garland le ha fallado a Estados Unidos?

Creo, un poco tarde en la vida, en dar oportunidades a la gente. Generosamente.

Language