Правильний спосіб спільного використання функцій між компонентами в React


90

У мене є кілька компонентів, котрі всі повинні робити одне і те ж. (Проста функція, яка відображає їх дочірні компоненти і робить щось для кожного з них). На даний момент я визначаю цей метод у кожному з компонентів. Але я хочу це визначити лише один раз.

Я міг би визначити його в компоненті верхнього рівня, а потім передати як проп. Але це не зовсім добре. Це більше бібліотечна функція, ніж prop. (Мені здається).

Який правильний спосіб зробити це?


Перевірте це посилання . Або шукайте в Google "міксини" та "HOC".
Борж

Відповіді:


36

Якщо ви використовуєте щось на зразок browserify, ви можете мати зовнішній файл, тобто util.js, який експортує деякі функції утиліти.

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

exports.doSomething = doSomething;

Потім вимагайте це за потреби

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

@AnkitSinghaniya Це залежить від того, що ви використовуєте для управління початковим станом вашого додатка?
deowk

1
Я використовую реакційні стани.
aks

Чому я отримую 'doSomething' не визначається без узагальнення будь-якої ідеї?
Абхіджіт Чакра,

45

Utils.js із найновішим синтаксисом Javascript ES6

Створіть такий Utils.jsфайл із кількома функціями тощо

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

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

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

Потім у своїх компонентах, які ви хочете використовувати утилітні функції, імпортуйте певні необхідні функції. Не потрібно імпортувати все

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

А потім використовуйте ці функції всередині компонента так:

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

Для чого призначений /fileкінець рядка імпорту?
alex

@alex Це лише приклад. Помістіть там свій відносний шлях до util.js
Фангмінг,

13

Якщо ви хочете маніпулювати станом у допоміжних функціях, дотримуйтесь цього:

  1. Створіть файл Helpers.js:

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

  2. Імпортуйте допоміжну функцію у файл компонента:

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

  3. У своєму конструкторі додайте цю допоміжну функцію до класу

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

  4. Використовуйте у своїй функції візуалізації:

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


10

Ось кілька прикладів того, як можна повторно використовувати функцію ( FetchUtil.handleError) у компоненті React ( App).

Рішення 1: Використання синтаксису модуля CommonJS

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

Рішення 2: Використання "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;

Примітка: Якщо ви використовуєте React v15.4 (або нижче), вам потрібно імпортувати createClassнаступним чином:

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

Джерело: https://reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactcreateclass

Компонент (який повторно використовує FetchUtil)

компоненти / 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

Ще одним надійним варіантом, крім створення файлу утиліти, було б використання компонента вищого порядку для створення withComponentMapper()обгортки. Цей компонент приймає компонент як параметр і повертає його назад із componentMapper()функцією, переданою як prop.

Це вважається гарною практикою в React. Детально це можна дізнатись тут.


Мені цікаво, чому React перешкоджає успадкуванню компонентів там, де шаблон withComponent здається схожим?
робочому режимі

@Wor Використання спадщини означає дві складові
Джейк,

4

Звучить як корисна функція, чому б тоді не помістити її в окремий статичний модуль?

В іншому випадку, якщо ви використовуєте транслятор, такий як Babel, ви можете використовувати статичні методи es7:

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

Або, якщо ви використовуєте React.createClass, ви можете використовувати об'єкт статики :

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

Однак я не раджу цих варіантів, немає сенсу включати компонент для корисного методу.

Крім того, ви не повинні передавати метод за допомогою всіх ваших компонентів, оскільки він підтримує їх щільність і робить рефакторинг більш болючим. Я раджу звичайний старий утилітний модуль.

Інший варіант - використовувати mixin для розширення класу, але я не рекомендую цього робити, оскільки ви не можете зробити це в es6 + (і я не бачу користі в цьому випадку).


Інформація про міксини є корисною. У цьому випадку я хочу умовно використовувати функцію, а не мати щось автоматично для події життєвого циклу. Тому. Як ви вже говорите, звичайна стара утиліта - це шлях.

1
Примітка: React.createClassзастаріло з React 15.5.0. app-response-app пропонує create-react-classзамість цього використовувати модуль npm .
Alex Johnson

Я прагну зробити те саме, що і OP, але тут я трохи заплутався. Ви не рекомендуєте жоден із перелічених вами варіантів. Що ж ви порадите?
kkuilla

Я б просто створив файл для функції, наприклад doSomething.js, або файл із кількома подібними функціями "утиліти", наприклад, utils.jsі імпортував би ті функції, де вам потрібно їх використовувати
Домінік,

4

Я покажу два стилі нижче, і ви захочете вибрати, залежно від того, наскільки логіка компонентів пов’язана між собою.

Стиль 1 - Відносно пов'язані компоненти можна створити за допомогою посилань зворотного виклику, наприклад, у ./components/App.js...

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

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

І тоді ви можете використовувати спільні функції між ними, як це ...

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

Стиль 2 - Компоненти типу Util можна створити таким чином, у ./utils/time.js...

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

І тоді їх можна використовувати ось так, у ./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"));
    }
}

Яким користуватися?

Якщо логіка відносно пов’язана (вони використовуються лише разом в одному додатку), то вам слід обмінюватися станами між компонентами. Але якщо ваша логіка пов'язана віддалено (тобто math util, util для форматування тексту), тоді вам слід створювати та імпортувати функції класу util.


2

Чи не слід використовувати для цього міксин? Див. Https://facebook.github.io/react/docs/reusable-components.html

Незважаючи на те, що вони впадають у немилість, див. Https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

Може бути корисним


Я погоджуюсь, що міксин тут є кращим рішенням, ніж просто експорт загальної функції.
Тао Хуан

@TaoHuang Деякі речі, які слід врахувати, 1.Mixins не є доказом у майбутньому, і вони будуть використовуватися все рідше і рідше в сучасних додатках, 2. Використання експортованої функції робить вашу структуру коду агностичною - ви можете легко використовувати ці функції в будь-якому іншому js-проекті. Також, будь ласка, прочитайте цю публікацію про те, чому НЕ використовувати Mixins -> facebook.github.io/react/blog/2016/07/13/…
deowk

Міксін може отримувати доступ і модифікувати стан, тоді як ознака цього не робить, і оскільки ОП каже, що вони хочуть щось розглядати як бібліотеку функцій, міксин не буде правильним рішенням.
HoldOffHunger

Щоб оновити це, використовуйте функції вищого порядку. facebook.github.io/react/docs/higher-order-components.html
Давет

Перша ланка мертва
Алекс,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.