Maneira correta de compartilhar funções entre componentes no React

92

Tenho vários componentes que precisam fazer a mesma coisa. (Uma função simples que mapeia seus componentes filhos e faz algo para cada um). No momento estou definindo este método em cada um dos componentes. Mas só quero definir uma vez.

Eu poderia defini-lo no componente de nível superior e depois passá-lo para baixo como um adereço. Mas isso não parece muito certo. É mais uma função de biblioteca do que um acessório. (Parece para mim).

Qual é a maneira correta de fazer isso?


fonte
Confira este link . Ou pesquise "mixins" e "HOC" no google.
Borzh

Respostas:

38

Se você usar algo como o browserify , poderá ter um arquivo externo, ou seja, util.js, que exporta algumas funções do utilitário.

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

exports.doSomething = doSomething;

Em seguida, solicite conforme necessário

var doSomething = require('./util.js').doSomething;
Deowk
fonte
@AnkitSinghaniya Isso depende, o que você está usando para gerenciar o estado de front end do seu aplicativo?
deowk
1
Estou usando estados de reação.
aks de
Por que estou recebendo 'doSomething' não está definido no-undef alguma ideia?
Abhijit Chakra
48

Utils.js com a sintaxe Javascript ES6 mais recente

Crie o Utils.jsarquivo assim com várias funções, etc.

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

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

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

Em seguida, nos componentes em que deseja usar as funções utilitárias, importe as funções específicas necessárias. Você não tem que importar tudo

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

Em seguida, use essas funções dentro do componente como este:

justAnAlert();
<p>{doSomethingWithInput('hello')}</p>
Fangming
fonte
1
Para que serve o /fileno final da linha de importação?
alex de
@alex É apenas um exemplo. Coloque seu caminho relativo para util.js lá
Fangming de
14

Se você deseja manipular o estado em funções auxiliares, siga isto:

  1. Crie um arquivo Helpers.js:

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

  2. Importar função auxiliar em seu arquivo de componente:

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

  3. Em seu construtor, adicione essa função auxiliar à classe

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

  4. Em sua função de renderização, use-o:

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

Jaskaran Singh
fonte
10

Aqui estão alguns exemplos de como você pode reutilizar uma função ( FetchUtil.handleError) em um componente React (App ).

Solução 1: usando a sintaxe do módulo CommonJS

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

Solução 2: usando "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;

Observação: se você estiver usando o React v15.4 (ou inferior), será necessário importar da createClassseguinte forma:

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

Fonte: 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
fonte
7

Outra opção sólida diferente de criar um arquivo util seria usar um componente de ordem superior para criar um withComponentMapper()wrapper. Este componente pegaria um componente como parâmetro e o retornaria com ocomponentMapper() função passada como um suporte.

Isso é considerado uma boa prática no React. Você pode descobrir como fazer isso em detalhes aqui.

Jake
fonte
Estou curioso para saber por que React desencoraja a herança de componentes, onde o padrão withComponent parece ser semelhante?
workwise
@Wor Usar herança significa os dois componentes
Jake
4

Parece uma função de utilitário, nesse caso, por que não colocá-lo em um módulo de utilitário estático separado?

Caso contrário, se estiver usando um transpiler como o Babel, você pode usar os métodos estáticos do es7:

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

Ou então, se você estiver usando React.createClass, poderá usar o objeto estático :

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

No entanto, não aconselho essas opções, não faz sentido incluir um componente para um método utilitário.

Além disso, você não deve passar um método por todos os seus componentes como um suporte, pois isso os unirá firmemente e tornará a refatoração mais dolorosa. Aconselho um módulo de utilitário simples e antigo.

A outra opção é usar um mixin para estender a classe, mas não recomendo, pois você não pode fazer isso no es6 + (e não vejo benefício neste caso).

Dominic
fonte
As informações sobre mixins são úteis. Nesse caso, quero usar condicionalmente a função, em vez de que algo aconteça automaticamente em um evento de ciclo de vida. Então. Como você diz, uma função de utilidade simples e antiga é o caminho a percorrer.
1
Nota: React.createClassestá obsoleto desde React 15.5.0. O create-react-app sugere o uso do módulo npm create-react-class.
Alex Johnson
Estou tentando fazer a mesma coisa que o OP, mas estou um pouco confuso aqui. Você não recomenda nenhuma das opções listadas. O que você recomenda?
kkuilla
Eu simplesmente faria um arquivo para a função doSomething.js, por exemplo , ou um arquivo com várias funções "utilitárias" semelhantes, por exemplo, utils.jse importaria essas funções onde você precisa usá-las
Dominic
4

Mostrarei dois estilos abaixo e você vai querer escolher dependendo de quanto a lógica dos componentes se relaciona entre si.

Estilo 1 - Os componentes relacionados podem ser criados com referências de retorno de chamada, como este, em ./components/App.js...

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

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

E então você pode usar funções compartilhadas entre eles assim ...

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 - os componentes do tipo utilitário podem ser criados assim, em ./utils/time.js...

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

E então eles podem ser usados assim, em ./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"));
    }
}

Qual usar?

Se a lógica for relativamente relacionada (eles só são usados ​​juntos no mesmo aplicativo), você deve compartilhar estados entre os componentes. Mas se a sua lógica for remotamente relacionada (ou seja, utilitário matemático, utilitário de formatação de texto), então você deve criar e importar funções de classe util.

HoldOffHunger
fonte
2

Você não deveria usar um Mixin para isso? Veja https://facebook.github.io/react/docs/reusable-components.html

Embora estejam caindo em desgraça, consulte https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

Pode ser útil

Davet
fonte
Concordo que mixin é uma solução melhor aqui do que apenas exportar uma função comum.
Tao Huang
@TaoHuang Algumas coisas a considerar, 1.Mixins não são à prova de futuro e serão usados ​​cada vez menos em aplicativos modernos, 2.Uso de uma função exportada torna sua estrutura de código agnóstica - você poderia facilmente usar essas funções em qualquer outro projeto js. Leia também este post sobre por que NÃO usar Mixins -> facebook.github.io/react/blog/2016/07/13/…
deowk
Um mixin pode acessar e modificar o estado, enquanto um traço não, e como o OP está dizendo que quer algo para tratar como uma biblioteca de funções, um mixin não seria a solução certa.
HoldOffHunger
Para atualizar isso, use funções de ordem superior. facebook.github.io/react/docs/higher-order-components.html
Davet 01 de
O primeiro link está morto
alex de