Forma correcta de compartir funciones entre componentes en React


90

Tengo varios componentes que deben hacer lo mismo. (Una función simple que mapea sus componentes secundarios y hace algo con cada uno). De momento estoy definiendo este método en cada uno de los componentes. Pero solo quiero definirlo una vez.

Podría definirlo en el componente de nivel superior y luego pasarlo como un accesorio. Pero eso no se siente del todo bien. Es más una función de biblioteca que un accesorio. (Me parece).

¿Cuál es la forma correcta de hacer esto?


Mira este enlace . O busque "mixins" y "HOC" en google.
Borzh

Respuestas:


36

Si usa algo como browserify , puede tener un archivo externo, es decir, util.js que exporta algunas funciones de utilidad.

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

exports.doSomething = doSomething;

Entonces solicítelo según sea necesario

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

@AnkitSinghaniya Depende, ¿qué estás usando para administrar el estado de la interfaz de tu aplicación?
deowk

1
Estoy usando estados de reacción.
aks

¿Por qué obtengo 'doSomething' no definido sin ninguna idea?
Abhijit Chakra

45

Utils.js con la última sintaxis de Javascript ES6

Crea el Utils.jsarchivo así con múltiples funciones, etc.

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

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

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

Luego, en los componentes que desea utilizar las funciones util, importe las funciones específicas que sean necesarias. No tienes que importar todo

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

Y luego use estas funciones dentro del componente como este:

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

¿Para qué sirve el /fileal final de la línea de importación?
Alex

@alex Es solo un ejemplo. Ponga su ruta relativa a util.js allí
Fangming

13

Si desea manipular el estado en las funciones auxiliares, siga esto:

  1. Cree un archivo Helpers.js:

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

  2. Importe la función auxiliar en su archivo de componente:

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

  3. En su constructor agregue esa función auxiliar a la clase

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

  4. En tu función de render, úsala:

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


10

Aquí hay algunos ejemplos sobre cómo puede reutilizar una función ( FetchUtil.handleError) en un componente React ( App).

Solución 1: uso de la sintaxis del módulo CommonJS

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

Solución 2: uso 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;

Nota: Si está utilizando React v15.4 (o inferior), debe importar de la createClasssiguiente manera:

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

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

Componente (que reutiliza FetchUtil)

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

Otra opción sólida además de crear un archivo util sería utilizar un componente de orden superior para crear un withComponentMapper()contenedor. Este componente tomaría un componente como parámetro y lo devolvería con la componentMapper()función transmitida como prop.

Esto se considera una buena práctica en React. Puede averiguar cómo hacerlo en detalle aquí.


Tengo curiosidad por saber por qué React desalienta la herencia de componentes donde el patrón withComponent parece ser similar.
workwise

@Wor Usar herencia significa los dos componentes
Jake

4

Suena como una función de utilidad, en ese caso, ¿por qué no ponerla en un módulo de utilidad estático separado?

De lo contrario, si usa un transpilador como Babel, puede hacer uso de los métodos estáticos de es7:

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

O bien, si está usando React.createClass, puede usar el objeto estático :

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

Sin embargo, no recomiendo esas opciones, no tiene sentido incluir un componente para un método de utilidad.

Además, no debería pasar un método a través de todos sus componentes como apoyo, ya que los acoplará estrechamente y hará que la refactorización sea más dolorosa. Aconsejo un módulo de utilidad simple y antiguo.

La otra opción es usar un mixin para extender la clase, pero no lo recomiendo ya que no puede hacerlo en es6 + (y no veo el beneficio en este caso).


La información sobre mixins es útil. En este caso, quiero usar condicionalmente la función en lugar de que algo suceda automáticamente en un evento del ciclo de vida. Entonces. Como usted dice, una simple función de utilidad es el camino a seguir.

1
Nota: React.createClassestá en desuso desde React 15.5.0. create-react-app sugiere el uso del módulo npm en su create-react-classlugar.
Alex Johnson

Estoy buscando hacer lo mismo que el OP, pero estoy un poco confundido aquí. No recomienda ninguna de las opciones que enumera. ¿ Qué recomiendas?
kkuilla

Simplemente haría un archivo para la función doSomething.js, por ejemplo , o un archivo con múltiples funciones de "utilidad" similares, por ejemplo, utils.jse importaría esas funciones donde necesite usarlas
Dominic

4

A continuación, mostraré dos estilos y querrá elegir según la relación entre la lógica de los componentes.

Estilo 1: componentes relativamente relacionados se pueden crear con referencias de devolución de llamada, como esta, en ./components/App.js...

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

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

Y luego puedes usar funciones compartidas entre ellos como este ...

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

Estilo 2: los componentes de tipo util pueden crear así, en ./utils/time.js...

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

Y luego pueden ser usar así, en ./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"));
    }
}

¿Cuál usar?

Si la lógica está relativamente relacionada (solo se usan juntos en la misma aplicación), entonces debe compartir estados entre componentes. Pero si su lógica está relacionada de forma distante (es decir, utilidad matemática, utilidad de formato de texto), entonces debe crear e importar funciones de clase util.


2

¿No deberías usar un Mixin para esto? Ver https://facebook.github.io/react/docs/reusable-components.html

Aunque están cayendo en desgracia, consulte https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

Podría ser útil


Estoy de acuerdo en que mixin es una mejor solución aquí que simplemente exportar una función común.
Tao Huang

@TaoHuang Algunas cosas a considerar, 1. Las mezclas no son a prueba de futuro y se usarán cada vez menos en las aplicaciones modernas, 2. El uso de una función exportada hace que su marco de código sea agnóstico; podría usar fácilmente esas funciones en cualquier otro proyecto js. También lea esta publicación sobre por qué NO usar Mixins -> facebook.github.io/react/blog/2016/07/13/…
deowk

Un mixin puede acceder y modificar el estado, mientras que un rasgo no lo hace, y dado que el OP dice que quieren algo para tratar como una biblioteca de funciones, un mixin no sería la solución correcta.
HoldOffHunger

Para actualizar esto, use funciones de orden superior. facebook.github.io/react/docs/higher-order-components.html
Davet

El primer enlace está muerto
Alex
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.