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

90

У меня есть несколько компонентов, которые должны делать одно и то же. (Простая функция, которая сопоставляет их дочерние компоненты и что-то делает с каждым из них). На данный момент я определяю этот метод в каждом из компонентов. Но я хочу дать ему определение только один раз.

Я мог бы определить его в компоненте верхнего уровня, а затем передать его как опору. Но это не совсем так. Это больше библиотечная функция, чем опора. (Мне кажется).

Как правильно это сделать?


источник
Ознакомьтесь с этой ссылкой . Или выполните поиск «миксинов» и «HOC» в Google.
Borzh

Ответы:

36

Если вы используете что-то вроде browserify, тогда у вас может быть внешний файл, например util.js, который экспортирует некоторые служебные функции.

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

exports.doSomething = doSomething;

Затем потребуйте его по мере необходимости

var doSomething = require('./util.js').doSomething;
Deowk
источник
@AnkitSinghaniya Это зависит от того, что вы используете для управления состоянием внешнего интерфейса вашего приложения?
deowk
1
Я использую состояния реакции.
aks
Почему я получаю «doSomething» не определено no-undef любая идея?
Abhijit Chakra
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>
Fangming
источник
Для чего стоит /fileконец строки импорта?
Алекс
@alex Это просто пример. Поместите туда свой относительный путь к util.js
Fangming
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

Другой надежный вариант, кроме создания файла util, - это использование компонента более высокого порядка для создания withComponentMapper()оболочки. Этот компонент примет компонент как параметр и вернет его обратно с componentMapper()функцией, переданной как опора.

В React это считается хорошей практикой. Подробнее о том, как это сделать, можно узнать здесь.

Джейк
источник
Мне любопытно, почему React препятствует наследованию компонентов, когда шаблон withComponent кажется похожим?
workwise 08
@Wor Использование наследования означает два компонента
Джейк
4

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

В противном случае, если вы используете транспилятор, такой как Babel, вы можете использовать статические методы es7:

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

Или, если вы используете React.createClass, вы можете использовать объект статики :

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

Однако я не советую эти варианты, нет смысла включать компонент для служебного метода.

Также вы не должны передавать метод через все ваши компоненты в качестве опоры, это будет тесно связывать их и сделать рефакторинг более болезненным. Советую простой старый служебный модуль.

Другой вариант - использовать миксин для расширения класса, но я не рекомендую этого делать, так как вы не можете этого сделать в es6 + (и я не вижу пользы в этом случае).

Доминик
источник
Информация о миксинах полезна. В этом случае я хочу использовать функцию условно, вместо того, чтобы что-то происходило автоматически при событии жизненного цикла. Так. Как вы говорите, обычная старая служебная функция - это правильный путь.
1
Примечание: React.createClassне рекомендуется, начиная с React 15.5.0. create-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 - компоненты служебного типа могут быть созданы таким образом в ./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"));
    }
}

Что использовать?

Если логика относительно взаимосвязана (они используются вместе только в одном приложении), тогда вы должны разделять состояния между компонентами. Но если ваша логика имеет отдаленное отношение (например, утилита для математики, утилита для форматирования текста), тогда вам следует создать и импортировать функции класса util.

HoldOffHunger
источник
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. Миксины не годятся для будущего и будут все меньше и меньше использоваться в современных приложениях. 2. Использование экспортируемой функции делает вашу платформу кода независимой - вы можете легко использовать эту функцию в любом другом проекте js. Также прочтите этот пост о том, почему НЕ использовать Mixins -> facebook.github.io/react/blog/2016/07/13/…
deowk
Примесь может обращаться к состоянию и изменять его, а признак - нет, и поскольку OP говорит, что они хотят, чтобы что-то рассматривалось как библиотека функций, миксин не был бы правильным решением.
HoldOffHunger
Чтобы обновить это, используйте функции высшего порядка. facebook.github.io/react/docs/higher-order-components.html
Davet
Первая ссылка мертва
alex