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?


fuente
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;
Deowk
fuente
@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>
Colmillos
fuente
¿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> }

Jaskaran Singh
fuente
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;
Benny Neugebauer
fuente
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í.

Jake
fuente
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).

Dominic
fuente
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.

HoldOffHunger
fuente
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

Davet
fuente
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