Avoir des services dans l'application React

177

Je viens du monde angulaire où je pourrais extraire la logique d'un service / usine et la consommer dans mes contrôleurs.

J'essaie de comprendre comment puis-je obtenir la même chose dans une application React.

Disons que j'ai un composant qui valide l'entrée du mot de passe de l'utilisateur (c'est la force). C'est la logique est assez complexe donc je ne veux pas l'écrire dans le composant lui-même.

Où dois-je écrire cette logique? Dans un magasin si j'utilise du flux? Ou y a-t-il une meilleure option?

Dennis Nerush
la source
Vous pouvez utiliser un package et voir comment ils le font - npmjs.com/package/react-password-strength-meter
James111
11
La force du mot de passe n'est qu'un exemple. Je recherche une meilleure pratique plus générale
Dennis Nerush
Vous devrez peut-être le faire côté serveur?
James111
2
Non. Seulement la logique côté client qui ne doit pas être directement dans le composant. Le vérificateur de force de mot de passe n'est qu'un exemple
Dennis Nerush
4
Si vous disposez de nombreuses fonctions de ce type, vous pouvez les stocker dans un fichier d'aide et simplement l'exiger dans votre fichier de composant pour utilisation. Si c'est une fonction unique qui concerne uniquement ce composant, elle devrait probablement y vivre, quelle que soit la complexité.
Jesse Kernaghan

Réponses:

61

La première réponse ne reflète pas le paradigme actuel Container vs Presenter .

Si vous avez besoin de faire quelque chose, comme valider un mot de passe, vous aurez probablement une fonction qui le fait. Vous passeriez cette fonction à votre vue réutilisable en tant qu'accessoire.

Conteneurs

Donc, la bonne façon de le faire est d'écrire un ValidatorContainer, qui aura cette fonction comme propriété, et d'y envelopper le formulaire, en passant les bons accessoires à l'enfant. En ce qui concerne votre vue, votre conteneur de validation encapsule votre vue et la vue utilise la logique des conteneurs.

La validation peut être entièrement effectuée dans les propriétés du conteneur, mais si vous utilisez un validateur tiers ou un service de validation simple, vous pouvez utiliser le service en tant que propriété du composant conteneur et l'utiliser dans les méthodes du conteneur. J'ai fait cela pour des composants reposants et cela fonctionne très bien.

Fournisseurs

S'il y a un peu plus de configuration nécessaire, vous pouvez utiliser un modèle fournisseur / consommateur. Un fournisseur est un composant de haut niveau qui s'enroule quelque part près de et sous l'objet d'application supérieur (celui que vous montez) et fournit une partie de lui-même, ou une propriété configurée dans la couche supérieure, à l'API de contexte. Je configure ensuite mes éléments de conteneur pour consommer le contexte.

Les relations de contexte parent / enfant n'ont pas besoin d'être proches les unes des autres, juste l'enfant doit être descendu d'une manière ou d'une autre. Redux stocke et le React Router fonctionne de cette manière. Je l'ai utilisé pour fournir un contexte reposant racine pour mes conteneurs de repos (si je ne fournis pas le mien).

(note: l'API context est marquée comme expérimentale dans la documentation, mais je ne pense pas que ce soit plus, compte tenu de ce qui l'utilise).

//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
};

Intergiciel

Un autre moyen que je n'ai pas essayé, mais que j'ai vu utilisé, est d'utiliser un middleware en conjonction avec Redux. Vous définissez votre objet de service en dehors de l'application, ou au moins, plus haut que le magasin redux. Lors de la création du magasin, vous injectez le service dans le middleware et le middleware gère toutes les actions qui affectent le service.

De cette façon, je pourrais injecter mon objet restful.js dans le middleware et remplacer mes méthodes de conteneur par des actions indépendantes. J'aurais toujours besoin d'un composant de conteneur pour fournir les actions à la couche de vue de formulaire, mais connect () et mapDispatchToProps m'ont couvert.

Le nouveau react-router-redux v4 utilise cette méthode pour impacter l'état de l'historique, par exemple.

//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)
  }
}

aphénine
la source
bonne réponse mec, tu m'as empêché de faire des trucs sans cervelle 8) KUDOS !!
csomakk
à quoi sert un exemple de conteneur?
sensei
Je ne le préconise pas, mais si vous souhaitez emprunter le chemin du localisateur de services (quelque chose de similaire à Angular), vous pouvez ajouter une sorte de fournisseur "injecteur / conteneur" à partir duquel vous résolvez les services (après les avoir enregistrés auparavant).
eddiewould
React hooks vient à la rescousse. Avec Hooks, vous pouvez écrire une logique réutilisable sans écrire de classe. reactjs.org/docs/…
Raja Malik
102

Le problème devient extrêmement simple lorsque vous réalisez qu'un service Angular n'est qu'un objet qui fournit un ensemble de méthodes indépendantes du contexte. C'est juste le mécanisme Angular DI qui le rend plus compliqué. La DI est utile car elle s'occupe de créer et de maintenir des instances pour vous mais vous n'en avez pas vraiment besoin.

Considérez une bibliothèque AJAX populaire nommée axios (dont vous avez probablement entendu parler):

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

Cela ne se comporte-t-il pas comme un service? Il fournit un ensemble de méthodes responsables d'une logique spécifique et est indépendant du code principal.

Votre exemple de cas concernait la création d'un ensemble isolé de méthodes pour valider vos entrées (par exemple, vérifier la force du mot de passe). Certains ont suggéré de mettre ces méthodes à l'intérieur des composants, ce qui pour moi est clairement un anti-pattern. Que faire si la validation implique d'effectuer et de traiter des appels backend XHR ou de faire des calculs complexes? Mélangeriez-vous cette logique avec des gestionnaires de clics de souris et d'autres éléments spécifiques à l'interface utilisateur? Absurdité. La même chose avec l'approche conteneur / HOC. Emballer votre composant juste pour ajouter une méthode qui vérifiera si la valeur contient un chiffre? Allons.

Je voudrais simplement créer un nouveau fichier nommé `` ValidationService.js '' et l'organiser comme suit:

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

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

export default ValidationService;

Puis dans votre composant:

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

...

//inside the component
yourInputChangeHandler(event) {

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

Utilisez ce service où que vous soyez. Si les règles de validation changent, vous devez vous concentrer uniquement sur le fichier ValidationService.js.

Vous pourriez avoir besoin d'un service plus compliqué qui dépend d'autres services. Dans ce cas, votre fichier de service peut renvoyer un constructeur de classe au lieu d'un objet statique afin que vous puissiez créer vous-même une instance de l'objet dans le composant. Vous pouvez également envisager d'implémenter un singleton simple pour vous assurer qu'il n'y a toujours qu'une seule instance de l'objet de service en cours d'utilisation dans l'ensemble de l'application.

Wojtek Majerski
la source
3
C'est ainsi que je le ferais aussi. Je suis assez surpris que cette réponse ait reçu si peu de voix, car cela semble être la voie avec le moins de friction. Si votre service dépend d'autres services, là encore, il importera ces autres services via leurs modules. De plus, les modules sont, par définition, des singletons, il n'y a donc en fait aucun travail supplémentaire à "implémenter comme un simple singleton" - vous obtenez ce comportement gratuitement :)
Mickey Puri
6
+1 - Bonne réponse si vous n'utilisez que des services qui fournissent des fonctions. Cependant , les services d'Angular sont des classes qui sont définies une fois, fournissant ainsi plus de fonctionnalités que de simples fonctions. Vous pouvez mettre en cache des objets en tant que paramètre de classe de service, par exemple.
Nino Filiu
6
Cela devrait être la vraie réponse, et non la réponse trop compliquée ci
user1807334
1
C'est une bonne réponse, sauf qu'elle n'est pas "réactive". Le DOM ne se mettra pas à jour sur les changements de variables au sein du service.
Defacto
9
Mais qu'en est-il de l'injection de dépendances? Il est impossible de se moquer du service dans votre composant à moins que vous ne l'injectiez d'une manière ou d'une autre. Peut-être qu'avoir un objet global «conteneur» de niveau supérieur qui a chaque service comme champ permettrait de contourner ce problème. Ensuite, dans vos tests, vous pouvez remplacer les champs de conteneurs par des simulations pour les services que vous souhaitez simuler.
menehune23
34

J'avais besoin d'une logique de formatage à partager entre plusieurs composants et en tant que développeur Angular, je me suis naturellement tourné vers un service.

J'ai partagé la logique en la mettant dans un fichier séparé

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

module.exports = {
    format: format
};

puis l'a importé en tant que module

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

//then in component

    render() {

        return formatter.format(this.props.data);
    }
Kildareflare
la source
8
C'est une bonne idée, comme mentionné dans le document React: reactjs.org/docs/composition-vs-inheritance.html Si vous souhaitez réutiliser des fonctionnalités non-UI entre les composants, nous vous suggérons de l'extraire dans un module JavaScript séparé. Les composants peuvent l'importer et utiliser cette fonction, cet objet ou cette classe sans l'étendre.
user3426603
C'est en fait la seule réponse ici qui ait du sens.
Artem Novikov le
33

Gardez à l'esprit que le but de React est de mieux coupler les choses qui devraient logiquement être couplées. Si vous concevez une méthode compliquée de «validation du mot de passe», où doit-elle être couplée?

Eh bien, vous allez devoir l'utiliser chaque fois que l'utilisateur doit saisir un nouveau mot de passe. Cela peut être sur l'écran d'enregistrement, un écran "mot de passe oublié", un écran "réinitialiser le mot de passe pour un autre utilisateur", etc.

Mais dans tous ces cas, il sera toujours lié à un champ de saisie de texte. C'est donc là que cela devrait être couplé.

Créez un très petit composant React composé uniquement d'un champ d'entrée et de la logique de validation associée. Entrez ce composant dans tous les formulaires qui pourraient souhaiter avoir une entrée de mot de passe.

C'est essentiellement le même résultat que d'avoir un service / une usine pour la logique, mais vous le couplez directement à l'entrée. Vous n'avez donc plus besoin de dire à cette fonction où chercher son entrée de validation, car elle est liée en permanence.

Jake Roby
la source
11
Qu'est-ce que c'est une mauvaise pratique de coupler la logique et l'interface utilisateur. Pour changer la logique, je vais devoir toucher le composant
Dennis Nerush
14
Réagissez fondamentalement aux défis de cette hypothèse que vous faites. C'est en contraste frappant avec l'architecture MVC traditionnelle. Cette vidéo explique très bien pourquoi (la section pertinente commence environ 2 minutes).
Jake Roby
8
Que faire si la même logique de validation doit également être appliquée à un élément de zone de texte? La logique doit encore être extraite dans un fichier partagé. Je ne pense pas qu'il existe une équivalence de la bibliothèque de réaction. Angular Service sont des injectables et le framework Angular est construit sur le modèle de conception d'injection de dépendances, qui autorise les instances des dépendances gérées par Angular. Lorsqu'un service est injecté, il y a généralement un singleton dans la portée fournie, pour avoir le même service dans React, une bibliothèque DI tierce doit être introduite dans l'application.
Downhillski
15
@gravityplanx J'aime utiliser React. Ce n'est pas un modèle angulaire, c'est un modèle de conception de logiciel. J'aime garder mon esprit ouvert tout en empruntant des choses que j'aime à d'autres bonnes parties.
Downhillski
1
Les modules @MickeyPuri ES6 ne sont pas les mêmes que l'injection de dépendances.
Spock
12

Je viens également de la région Angular.js et les services et usines dans React.js sont plus simples.

Vous pouvez utiliser des fonctions ou des classes simples, un style de rappel et un événement Mobx comme moi :)

// 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>

Voici un exemple simple:

Juraj
la source
React.js est une bibliothèque d'interface utilisateur pour rendre et organiser les composants d'interface utilisateur. Lorsqu'il s'agit de services qui peuvent nous aider à ajouter des fonctionnalités supplémentaires, nous devons créer des collections de fonctions, d'objets fonctionnels ou de classes. J'ai trouvé les classes très utiles mais je sais que je joue aussi avec un style fonctionnel qui peut également être utilisé pour créer des helpers pour ajouter des fonctionnalités avantageuses qui sont hors de la portée de Reac.js.
Juraj
Je viens de mettre en œuvre cela. La façon dont vous en avez fait une classe et exportée est assez élégante.
GavinBelson le
10

Même situation: Après avoir réalisé plusieurs projets Angular et être passé à React, ne pas avoir un moyen simple de fournir des services via DI semble être une pièce manquante (les détails du service mis à part).

En utilisant le contexte et les décorateurs ES7, nous pouvons nous en approcher:

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

Il semble que ces gars-là ont fait un pas de plus / dans une direction différente:

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

On a toujours envie de travailler à contre-courant. Revisitera cette réponse dans 6 mois après avoir entrepris un grand projet React.

EDIT: De retour 6 mois plus tard avec un peu plus d'expérience React. Considérez la nature de la logique:

  1. Est-il lié (uniquement) à l'interface utilisateur? Déplacez-le dans un composant (réponse acceptée).
  2. Est-ce lié (uniquement) à la gestion de l'État? Déplacez-le dans un bruit sourd .
  3. Lié aux deux? Déplacer vers un fichier séparé, consommer en composant via un sélecteur et en thunks.

Certains atteignent également des HOC pour la réutilisation, mais pour moi, ce qui précède couvre presque tous les cas d'utilisation. Envisagez également de mettre à l'échelle la gestion de l'état à l'aide de canards pour séparer les préoccupations et les états centrés sur l'interface utilisateur.

corolle
la source
Imho je pense qu'il existe un moyen simple de fournir des services via DI, en utilisant le système de module ES6
Mickey Puri
1
@MickeyPuri, le module DI ES6 n'inclurait pas la nature hiérarchique de Angular DI, c'est-à-dire. parents (dans DOM) instanciant et remplaçant les services fournis aux composants enfants. Le module DI Imho ES6 se compare plus proche des systèmes DI backend comme Ninject et Structuremap, assis à l'écart de la hiérarchie des composants DOM plutôt que d'être basé sur celle-ci. Mais j'aimerais savoir ce que vous en pensez.
corolla
6

Je viens également d'Angular et j'essaie React, à partir de maintenant, une méthode recommandée (?) Semble être d'utiliser des composants d'ordre élevé :

Un composant d'ordre supérieur (HOC) est une technique avancée dans React pour réutiliser la logique des composants. Les HOC ne font pas partie de l'API React en soi. Ils sont un modèle qui émerge de la nature compositionnelle de React.

Disons que vous avez inputet textareaaimez appliquer la même logique de validation:

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>
)

Ensuite, écrivez un HOC qui valide et stylise le composant enveloppé:

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} />
    }
  }
}

Désormais, ces HOC partagent le même comportement de validation:

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

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

J'ai créé une simple démo .

Edit : Une autre démonstration utilise des accessoires pour transmettre un tableau de fonctions afin que vous puissiez partager une logique composée de plusieurs fonctions de validation à travers des HOCs comme:

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

Edit2 : React 16.8+ fournit une nouvelle fonctionnalité, Hook , une autre manière intéressante de partager la logique.

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

bob
la source
Je vous remercie. J'ai vraiment appris de cette solution. Et si j'ai besoin de plus d'un validateur. Par exemple, en plus du validateur de 3 lettres, que se passe-t-il si je veux avoir un autre validateur qui s'assure qu'aucun chiffre n'est entré. Pourrait-on composer des validateurs?
Youssef Sherif
1
@YoussefSherif Vous pouvez préparer plusieurs fonctions de validation et les passer comme accessoires de HOC, voir mon édition pour une autre démo.
bob
donc HOC est essentiellement un composant de conteneur?
sensei
Oui, de React doc: "Notez qu'un HOC ne modifie pas le composant d'entrée, ni n'utilise l'héritage pour copier son comportement. Au contraire, un HOC compose le composant d'origine en l'enveloppant dans un composant conteneur. Un HOC est un pur fonction sans effets secondaires. "
bob
1
L'exigence était d'injecter de la logique, je ne vois pas pourquoi nous avons besoin d'un HOC pour faire cela. Bien que vous puissiez le faire avec un HOC, cela semble trop compliqué. Ma compréhension des HOC est quand il y a aussi un état supplémentaire qui doit être ajouté et géré, c'est-à-dire pas de pure logique (ce qui était le cas ici).
Mickey Puri
4

Le service n'est pas limité à Angular, même en Angular2 + ,

Le service est juste une collection de fonctions d'assistance ...

Et il existe de nombreuses façons de les créer et de les réutiliser dans l'application ...

1) Ils peuvent être toutes des fonctions séparées qui sont exportées à partir d'un fichier js, comme ci-dessous:

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

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

2) Nous pouvons également utiliser une méthode d'usine comme, avec collection de fonctions ... avec ES6, il peut s'agir d'une classe plutôt que d'un constructeur de fonction:

class myService {

  constructor() {
    this._data = null;
  }

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

  getMyService() {
    return this._data;
  }

}

Dans ce cas, vous devez créer une instance avec une nouvelle clé ...

const myServiceInstance = new myService();

Dans ce cas également, chaque instance a sa propre vie, alors soyez prudent si vous souhaitez la partager, dans ce cas, vous ne devez exporter que l'instance que vous souhaitez ...

3) Si votre fonction et vos utils ne seront pas partagés, vous pouvez même les mettre dans le composant React, dans ce cas, tout comme la fonction dans votre composant react ...

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

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

4) Une autre façon de gérer les choses pourrait être d'utiliser Redux , c'est un magasin temporaire pour vous, donc si vous l'avez dans votre application React , cela peut vous aider avec de nombreuses fonctions de setter getter vous utilisez ... C'est comme un grand magasin qui gardent une trace de vos états et peuvent le partager entre vos composants, vous pouvez donc vous débarrasser de beaucoup de problèmes liés aux éléments de setter que nous utilisons dans les services ...

Il est toujours bon de faire un code DRY et de ne pas répéter ce qui doit être utilisé pour rendre le code réutilisable et lisible, mais n'essayez pas de suivre les méthodes angulaires dans l'application React , comme mentionné au point 4, l'utilisation de Redux peut réduire votre besoin de services et vous limitez leur utilisation pour certaines fonctions d'assistance réutilisables comme l'élément 1 ...

Alireza
la source
Bien sûr, vous pouvez le trouver sur mon site Web personnel qui est un lien depuis ma page de profil ...
Alireza
"Ne suivez pas les méthodes angulaires dans React" .. ahem Angular favorise l'utilisation de Redux et diffuse le magasin vers les composants de présentation en utilisant Observables et la gestion des états de type Redux comme RxJS / Store. .. Voulez-vous dire AngularJS? Parce que c'est une autre chose
Spock
1

Je suis dans la même botte que toi. Dans le cas que vous mentionnez, j'implémenterais le composant d'interface utilisateur de validation d'entrée en tant que composant React.

Je suis d'accord que la mise en œuvre de la logique de validation elle-même ne doit (doit) pas être couplée. Par conséquent, je le mettrais dans un module JS séparé.

Autrement dit, pour la logique qui ne doit pas être couplée, utilisez un module / une classe JS dans un fichier séparé et utilisez require / import pour dissocier le composant du "service".

Cela permet l'injection de dépendances et le test unitaire des deux indépendamment.

Sibidiba
la source
1

ou vous pouvez injecter l'héritage de classe "http" dans React Component

via l'objet d'accessoires.

  1. mettre à jour :

    ReactDOM.render(<ReactApp data={app} />, document.getElementById('root'));
  2. Modifiez simplement React Component ReactApp comme ceci:

    class ReactApp extends React.Component {
    
    state = {
    
        data: ''
    
    }
    
        render(){
    
        return (
            <div>
            {this.props.data.getData()}      
            </div>
    
        )
        }
    }
Juraj
la source
0

Eh bien, le modèle le plus utilisé pour la logique réutilisable que j'ai rencontré est l'écriture d'un hook ou la création d'un fichier utils. Cela dépend de ce que vous voulez accomplir.

hooks/useForm.js

Comme si vous voulez valider les données de formulaire, je créerais un hook personnalisé nommé useForm.js et lui fournirais des données de formulaire et en retour, il me renverrait un objet contenant deux choses:

Object: {
    value,
    error,
}

Vous pouvez certainement en retourner plus de choses au fur et à mesure que vous progressez.

utils/URL.js

Un autre exemple serait que vous vouliez extraire des informations d'une URL, puis je créerais un fichier utils pour celui-ci contenant une fonction et l'importerais si nécessaire:

 export function getURLParam(p) {
...
}
Muhammad Shahryar
la source