Poprawny sposób udostępniania funkcji między komponentami w Reakcie


90

Mam wiele komponentów, z których wszystkie muszą robić to samo. (Prosta funkcja, która odwzorowuje ich komponenty potomne i robi coś z każdym z nich). W tej chwili definiuję tę metodę w każdym z komponentów. Ale chcę to zdefiniować tylko raz.

Mógłbym to zdefiniować w komponencie najwyższego poziomu, a następnie przekazać jako rekwizyt. Ale to nie wydaje się całkiem w porządku. Jest to bardziej funkcja biblioteczna niż rekwizyt. (Wydaje mi się).

Jaki jest właściwy sposób zrobienia tego?


Sprawdź ten link . Lub wyszukaj „mixins” i „HOC” w Google.
Borzh

Odpowiedzi:


36

Jeśli używasz czegoś takiego jak browserify , możesz mieć zewnętrzny plik, np. Util.js, który eksportuje niektóre funkcje narzędziowe.

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

exports.doSomething = doSomething;

Następnie wymagaj tego w razie potrzeby

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

@AnkitSinghaniya To zależy, czego używasz do zarządzania stanem interfejsu użytkownika aplikacji?
Deowk

1
Używam stanów reagowania.
aks

Dlaczego otrzymuję „doSomething” nie jest zdefiniowane jako nie-undef żaden pomysł?
Abhijit Chakra

45

Utils.js z najnowszą składnią Javascript ES6

Utwórz Utils.jsplik w ten sposób z wieloma funkcjami itp

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

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

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

Następnie w komponentach, które mają być używane z funkcjami util, zaimportuj potrzebne funkcje. Nie musisz importować wszystkiego

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

Następnie użyj tych funkcji w komponencie w następujący sposób:

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

Do czego służy /filekoniec wiersza importu?
Alex

@alex To tylko przykład. Umieść swoją względną ścieżkę do pliku util.js
Fangming

13

Jeśli chcesz manipulować stanem w funkcjach pomocniczych, wykonaj następujące czynności:

  1. Utwórz plik Helpers.js:

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

  2. Importuj funkcję pomocniczą w pliku komponentu:

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

  3. W swoim konstruktorze dodaj tę funkcję pomocniczą do klasy

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

  4. W swojej funkcji renderującej użyj tego:

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


10

Oto kilka przykładów, w jaki sposób można ponownie użyć funkcji ( FetchUtil.handleError) w komponencie React ( App).

Rozwiązanie 1: Korzystanie ze składni modułu CommonJS

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

Rozwiązanie 2: Używanie „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;

Uwaga: Jeśli używasz React v15.4 (lub starszej), musisz zaimportować createClassw następujący sposób:

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

Źródło: https://reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactcreateclass

Komponent (który ponownie wykorzystuje FetchUtil)

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

Inną solidną opcją, inną niż tworzenie pliku util, byłoby użycie komponentu wyższego rzędu do utworzenia withComponentMapper()opakowania. Ten komponent wziąłby komponent jako parametr i zwróciłby go z componentMapper()funkcją przekazaną jako właściwość .

Jest to uważane za dobrą praktykę w React. Możesz dowiedzieć się, jak to zrobić szczegółowo tutaj.


Ciekaw jestem, dlaczego React odradza dziedziczenie komponentów, gdzie wzorzec withComponent wydaje się być podobny?
workwise

@Wor Używanie dziedziczenia oznacza dwa składniki
Jake,

4

Brzmi jak funkcja narzędzia, w takim razie dlaczego nie umieścić jej w oddzielnym statycznym module narzędziowym?

W przeciwnym razie, jeśli używasz transpilera, takiego jak Babel, możesz użyć statycznych metod es7:

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

Albo jeśli używasz React.createClass można użyć statyki obiektu:

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

Jednak nie radzę takich opcji, nie ma sensu włączać komponentu do metody użytkowej.

Nie powinieneś także przekazywać metody do wszystkich komponentów jako rekwizytu, ponieważ ściśle je połączy i sprawi, że refaktoryzacja będzie bardziej bolesna. Radzę zwykły stary moduł narzędziowy.

Inną opcją jest użycie miksera do rozszerzenia klasy, ale nie polecam tego, ponieważ nie możesz tego zrobić w es6 + (i nie widzę korzyści w tym przypadku).


Informacje o mieszankach są przydatne. W tym przypadku chcę warunkowo używać tej funkcji, zamiast mieć coś, co dzieje się automatycznie w przypadku zdarzenia cyklu życia. Więc. Jak mówisz, droga do zastosowania jest zwykła stara funkcja użyteczności.

1
Uwaga: React.createClassjest przestarzałe od React 15.5.0. create-aware-app create-react-classzamiast tego sugeruje użycie modułu npm .
Alex Johnson,

Chcę zrobić to samo, co OP, ale jestem trochę zdezorientowany. Nie polecasz żadnej z wymienionych opcji. Czego nie polecacie?
kkuilla

Po prostu utworzyłbym plik dla funkcji, np. doSomething.jsLub plik z wieloma podobnymi funkcjami "narzędziowymi", np. utils.jsI zaimportował te funkcje, gdzie trzeba ich użyć
Dominic

4

Poniżej pokażę dwa style i będziesz chciał wybrać w zależności od tego, jak bardzo logika składników jest ze sobą powiązana.

Styl 1 - Względnie powiązane komponenty można tworzyć z odwołaniami zwrotnymi, takimi jak ten, w ./components/App.js...

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

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

A potem możesz używać wspólnych funkcji między nimi w ten sposób ...

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

Styl 2 - Komponenty typu użytkowego można tworzyć w ten sposób w ./utils/time.js...

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

A potem mogą być używane w ten sposób, w ./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"));
    }
}

Którego użyć?

Jeśli logika jest względnie powiązana (są używane tylko razem w tej samej aplikacji), należy udostępniać stany między komponentami. Ale jeśli twoja logika jest odlegle powiązana (np. Narzędzie matematyczne, narzędzie do formatowania tekstu), powinieneś utworzyć i zaimportować funkcje klasy util.


2

Nie powinieneś używać do tego Mixina? Zobacz https://facebook.github.io/react/docs/reusable-components.html

Chociaż wypadają z łask, zobacz https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

Może się przydać


Zgadzam się, że mixin jest tutaj lepszym rozwiązaniem niż tylko eksportowanie wspólnej funkcji.
Tao Huang

@TaoHuang Kilka rzeczy do rozważenia: 1.Miksiny nie są przyszłościowe i będą coraz rzadziej używane w nowoczesnych aplikacjach, 2.Używanie wyeksportowanej funkcji sprawia, że ​​twój kod jest agnostyczny - możesz łatwo użyć tych funkcji w każdym innym projekcie js Przeczytaj również ten post o tym, dlaczego NIE używać Mixinów -> facebook.github.io/react/blog/2016/07/13/ ...
deowk

Mikser może uzyskiwać dostęp i modyfikować stan, podczas gdy cecha nie, a ponieważ OP mówi, że chce czegoś, co można traktować jako bibliotekę funkcji, mixin nie byłby właściwym rozwiązaniem.
HoldOffHunger

Aby to zaktualizować, użyj funkcji wyższego rzędu. facebook.github.io/react/docs/higher-order-components.html
Davet

Pierwszy link nie działa
alex
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.