Bonne façon de partager les fonctions entre les composants dans React


90

J'ai plusieurs composants qui doivent tous faire la même chose. (Une fonction simple qui mappe sur leurs composants enfants et fait quelque chose à chacun). En ce moment, je définis cette méthode dans chacun des composants. Mais je ne veux le définir qu'une seule fois.

Je pourrais le définir dans le composant de niveau supérieur, puis le transmettre comme accessoire. Mais cela ne semble pas tout à fait correct. C'est plus une fonction de bibliothèque qu'un accessoire. (Il me semble).

Quelle est la bonne façon de procéder?


Consultez ce lien . Ou recherchez "mixins" et "HOC" dans google.
Borzh

Réponses:


36

Si vous utilisez quelque chose comme browserify, vous pouvez avoir un fichier externe ie util.js qui exporte certaines fonctions utilitaires.

var doSomething = function(num) {
 return num + 1;
}

exports.doSomething = doSomething;

Ensuite, exigez-le au besoin

var doSomething = require('./util.js').doSomething;

@AnkitSinghaniya Cela dépend, qu'utilisez-vous pour gérer l'état frontal de votre application?
deowk

1
J'utilise des états de réaction.
aks

Pourquoi je reçois 'doSomething' n'est pas défini sans aucune idée?
Abhijit Chakra

45

Utils.js avec la dernière syntaxe Javascript ES6

Créez le Utils.jsfichier comme celui-ci avec plusieurs fonctions, etc.

const someCommonValues = ['common', 'values'];

export const doSomethingWithInput = (theInput) => {
   //Do something with the input
   return theInput;
};

export const justAnAlert = () => {
   alert('hello');
};

Ensuite, dans vos composants que vous souhaitez utiliser les fonctions util, importez les fonctions spécifiques nécessaires. Vous n'avez pas à tout importer

import {doSomethingWithInput, justAnAlert} from './path/to/utils.js/file'

Et puis utilisez ces fonctions dans le composant comme ceci:

justAnAlert();
<p>{doSomethingWithInput('hello')}</p>

À quoi sert la /filefin de la ligne d'importation?
alex le

@alex C'est juste un exemple. Mettez votre chemin relatif vers le util.js là
Fangming

13

Si vous souhaitez manipuler l'état dans les fonctions d'assistance, procédez comme suit:

  1. Créez un fichier Helpers.js:

    export function myFunc(){ return this.state.name; //define it according to your needs }

  2. Importez la fonction d'assistance dans votre fichier de composant:

    import {myFunc} from 'path-to/Helpers.js'

  3. Dans votre constructeur, ajoutez cette fonction d'assistance à la classe

    constructor(){ this.myFunc = myFunc.bind(this) }

  4. Dans votre fonction de rendu, utilisez-le:

    render(){ <div>{this.myFunc()}</div> }


10

Voici quelques exemples sur la façon dont vous pouvez réutiliser une fonction ( FetchUtil.handleError) dans un composant React ( App).

Solution 1: Utilisation de la syntaxe du module CommonJS

module.exports = {
  handleError: function(response) {
    if (!response.ok) throw new Error(response.statusText);
    return response;
  },
};

Solution 2: Utilisation de "createClass" (React v16)

util / FetchUtil.js

const createReactClass = require('create-react-class');

const FetchUtil = createReactClass({
  statics: {
    handleError: function(response) {
      if (!response.ok) throw new Error(response.statusText);
      return response;
    },
  },
  render() {
  },
});

export default FetchUtil;

Remarque: si vous utilisez React v15.4 (ou une version antérieure), vous devez importer createClasscomme suit:

import React from 'react';
const FetchUtil = React.createClass({});

Source: https://reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactcreateclass

Composant (qui réutilise FetchUtil)

composants / App.jsx

import Categories from './Categories.jsx';
import FetchUtil from '../utils/FetchUtil';
import Grid from 'material-ui/Grid';
import React from 'react';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {categories: []};
  }

  componentWillMount() {
    window
      .fetch('/rest/service/v1/categories')
      .then(FetchUtil.handleError)
      .then(response => response.json())
      .then(categories => this.setState({...this.state, categories}));
  }

  render() {
    return (
      <Grid container={true} spacing={16}>
        <Grid item={true} xs={12}>
          <Categories categories={this.state.categories} />
        </Grid>
      </Grid>
    );
  }
}

export default App;

7

Une autre option solide autre que la création d'un fichier util serait d'utiliser un composant d'ordre supérieur pour créer un withComponentMapper()wrapper. Ce composant prendrait un composant comme paramètre et le renverrait avec la componentMapper()fonction transmise comme accessoire.

Ceci est considéré comme une bonne pratique dans React. Vous pouvez découvrir comment procéder en détail ici.


Je suis curieux de savoir pourquoi React décourage l'héritage des composants là où le modèle withComponent semble être similaire?
workwise

@Wor L'utilisation de l'héritage signifie les deux composants
Jake

4

Cela ressemble à une fonction utilitaire, dans ce cas, pourquoi ne pas la placer dans un module utilitaire statique séparé?

Sinon, si vous utilisez un transpilateur comme Babel, vous pouvez utiliser les méthodes statiques d'es7:

class MyComponent extends React.Component {
  static someMethod() { ...

Ou bien si vous utilisez React.createClass, vous pouvez utiliser l' objet statique :

var MyComponent = React.createClass({
  statics: {
    customMethod: function(foo) {
      return foo === 'bar';
    }
  }

Cependant, je ne conseille pas ces options, cela n'a pas de sens d'inclure un composant pour une méthode utilitaire.

De plus, vous ne devriez pas passer une méthode à travers tous vos composants comme un accessoire, cela les couplera étroitement et rendra la refactorisation plus douloureuse. Je conseille un simple module utilitaire ancien.

L'autre option est d'utiliser un mixin pour étendre la classe, mais je ne le recommande pas car vous ne pouvez pas le faire dans es6 + (et je ne vois pas l'avantage dans ce cas).


Les informations sur les mixins sont utiles. Dans ce cas, je souhaite utiliser la fonction de manière conditionnelle plutôt que que quelque chose se produise automatiquement lors d'un événement du cycle de vie. Donc. Comme vous le dites, une fonction utilitaire ancienne est la voie à suivre.

1
Remarque: React.createClassest obsolète depuis React 15.5.0. create-react-app suggère d'utiliser le module npm à la create-react-classplace.
Alex Johnson le

Je cherche à faire la même chose que l'OP mais je suis un peu confus ici. Vous ne recommandez aucune des options que vous répertoriez. Que ne recommandez - vous?
kkuilla

Je ferais simplement un fichier pour la fonction, par exemple doSomething.js, ou un fichier avec plusieurs fonctions "utilitaires" similaires, par exemple utils.jset importer ces fonctions là où vous devez les utiliser
Dominic

4

Je vais montrer deux styles ci-dessous, et vous voudrez choisir en fonction de la relation entre la logique des composants.

Style 1 - Des composants relativement liés peuvent être créés avec des références de rappel, comme ceci, dans ./components/App.js...

<SomeItem
    ref={(instance) => {this.childA = instance}}
/>

<SomeOtherItem
    ref={(instance) => {this.childB = instance}}
/>

Et puis vous pouvez utiliser des fonctions partagées entre eux comme ça ...

this.childA.investigateComponent(this.childB);  // call childA function with childB as arg
this.childB.makeNotesOnComponent(this.childA);  // call childB function with childA as arg

Style 2 - Les composants de type Util peuvent être créés comme ceci, dans ./utils/time.js...

export const getTimeDifference = function (start, end) {
    // return difference between start and end
}

Et puis ils peuvent être utilisés comme ça, dans ./components/App.js...

import React from 'react';
import {getTimeDifference} from './utils/time.js';

export default class App extends React.Component {
    someFunction() {
        console.log(getTimeDifference("19:00:00", "20:00:00"));
    }
}

Lequel utiliser?

Si la logique est relativement liée (elles ne sont utilisées que dans la même application), vous devez partager les états entre les composants. Mais si votre logique est distante (c'est -à- dire, utilitaire mathématique, utilitaire de formatage de texte), alors vous devez créer et importer des fonctions de classe util.


2

Ne devriez-vous pas utiliser un Mixin pour cela? Voir https://facebook.github.io/react/docs/reusable-components.html

Bien qu'ils tombent en disgrâce, voir https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

Cela pourrait être utile


Je suis d'accord que mixin est une meilleure solution ici que simplement exporter une fonction commune.
Tao Huang

@TaoHuang Quelques points à prendre en compte, 1.Les mixins ne sont pas à l'épreuve du temps et seront de moins en moins utilisés dans les applications modernes, 2.L'utilisation d'une fonction exportée rend votre framework de code indépendant - vous pouvez facilement utiliser ces fonctions sur n'importe quel autre projet js. Veuillez également lire cet article sur pourquoi NE PAS utiliser Mixins -> facebook.github.io/react/blog/2016/07/13/…
deowk

Un mixin peut accéder et modifier l'état, alors qu'un trait ne le fait pas, et puisque l'OP dit qu'il veut quelque chose à traiter comme une bibliothèque de fonctions, un mixin ne serait pas la bonne solution.
HoldOffHunger

Pour mettre à jour cela, utilisez des fonctions d'ordre supérieur. facebook.github.io/react/docs/higher-order-components.html
Davet

Le premier lien est mort
alex le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.