В небольших проектах React хорошо работает хранение всех ваших методов компонентов в самих компонентах. В проектах среднего размера вы можете захотеть получить эти методы из своих компонентов и превратить их в «помощника». Здесь я покажу вам, как использовать класс (вместо экспорта отдельных функций и переменных) для организации вашего кода.

Примечание. Я работаю в React, поэтому мы обсудим этот пример.

Типичный рефакторинг

В типичном рефакторинге вы берете функцию компонента и перемещаете ее другому помощнику.

Из:

const MyComponent = () => {
  const someFunction = () => 'Hey, I am text'
  return (
    <div>
      {someFunction()}
    </div>
  )
}

To:

import { someFunction } from 'functionHelper.js'
const MyComponent = () => {
  return (
    <div>
      {someFunction()}
    </div>
  )
}

а также

export const someFunction = () => 'Hey, I am text'

Этот пример действительно глупый, но вы видите, к чему мы идем:

  1. Возьмите свои функции и скопируйте их в отдельный файл
  2. Импортируйте их и вызывайте как обычно.

Однако, когда ситуация усложняется, вам придется передать кучу вещей этим функциям - объекты, функции для управления состоянием и так далее. Сегодня я столкнулся с проблемой, когда я хотел извлечь три функции из компонента, и все они требовали одинаковых входных данных (resource и функция для обновления resource). Должен быть способ получше ...

Рефакторинг с классом

Я сделал большую демонстрацию для этого поста. Вы можете увидеть код на Github. Первоначальная фиксация показывает всю функциональность внутри основного компонента (App.js), а последующая фиксация выполняет рефакторинг кода для использования класса.

Вы можете запустить это самостоятельно и делать с этим все, что захотите. Не забудьте yarn install.

Мы начинаем с компонента, который «извлекает» объект (имитируя способ, которым мы могли бы сделать это из API) с определенными атрибутами на нем: повторение (количество ящиков), сторона (высота и ширина), текст, цвет. Затем у нас есть несколько способов манипулирования представлением - изменение цвета, обновление текста и т. Д. После каждого изменения мы отображаем сообщение.

Например, вот наш метод изменения ширины и высоты:

changeSide = side => {
  const obj = {...this.state.obj, side}
  this.fetchObject(obj);
  this.setState({ message: `You changed the sides to ${side} pixels!` });
}

У нас может быть ряд других методов, для которых требуются аналогичные действия - или, возможно, совсем другие методы. Мы могли бы начать думать об извлечении этого кода в помощник. Затем мы создадим другой метод для вызова действия setState, и нам нужно будет передать его this.fetchObject, объект в состоянии и side, который мы получаем в качестве аргумента метода. Если у нас есть несколько похожих методов, это очень много передаваемых параметров, и, возможно, это не так полезно (или не читается).

Вместо этого мы можем использовать класс в комплекте с методом конструктора:

export default class ObjectManipulator {
  constructor( { object, fetchObject, markResettable, updateMessage, updateStateValue } ) {
    this.fetchObject = fetchObject;
    this.markResettable = markResettable;
    this.updateMessage = updateMessage;
    this.updateStateValue = updateStateValue;
  }

  changeSide = ( object, side ) => {
    const newObject = { ...object, side };
    this.fetchObject(newObject);
    this.updateMessage(`You changed the sides to ${side} pixels!`);
    this.markResettable();
    this.updateStateValue('side', side);
  };
};

Это позволяет нам создать объект, функции которого мы можем вызывать внутри нашего основного компонента:

const manipulator = new ObjectManipulator({
  object,
  fetchObject: this.fetchObject,
  markResettable: this.markResettable,
  updateMessage: this.updateMessage,
  updateStateValue: this.updateStateValue,
});

Это создает объект manipulator - экземпляр нашего класса ObjectManipulator. Когда мы вызываем manipulator.changeSide(object, '800'), он запускает метод changeSide, который мы определили выше. Нет необходимости передавать updateMessage или какие-либо другие методы - мы получаем их из конструктора при создании экземпляра.

Вы можете себе представить, что это станет действительно полезным, если у нас будет много таких методов. В моем случае мне нужно было позвонить .then(res => myFunction(res) после всего, что я пытался извлечь. Определение myFunction в экземпляре класса вместо передачи его каждой функции сэкономило мне много кода.

Держать все организованным

Этот метод организации может быть действительно полезным, чтобы все было на своих местах. Например, у меня есть массив цветов, который я накладываю, чтобы получить цветные кнопки, которые вы видите в примере. Переместив эту константу в ObjectManipulator, я могу убедиться, что она не конфликтует с другими colors в остальной части моего приложения:

export default class ObjectManipulator {
  [...]

  colors = ['blue', 'red', 'orange', 'aquamarine', 'green', 'gray', 'magenta'];
};

Я могу использовать manipulator.colors, чтобы выбрать правильные цвета для этой страницы, тогда как может быть глобальная константа colors, которая используется для чего-то другого.

использованная литература

Старая добрая документация Mozilla Class