В небольших проектах 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'
Этот пример действительно глупый, но вы видите, к чему мы идем:
- Возьмите свои функции и скопируйте их в отдельный файл
- Импортируйте их и вызывайте как обычно.
Однако, когда ситуация усложняется, вам придется передать кучу вещей этим функциям - объекты, функции для управления состоянием и так далее. Сегодня я столкнулся с проблемой, когда я хотел извлечь три функции из компонента, и все они требовали одинаковых входных данных (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
, которая используется для чего-то другого.