#javascript #reactjs
#javascript #reactjs
Вопрос:
После того, как я прочитал, насколько плохи миксины, и сам получил несколько неудачных опытов, я хочу реорганизовать свой код, чтобы использовать композицию вместо миксинов для достижения возможности повторного использования.
Я хочу, чтобы компонент генерировал события и был стилизованным. Итак, у меня есть mixins EventEmitter, который содержит функции для управления событиями, и Stylable, который содержит функции для управления стилем.
var Styleable = {
addStyleProperty: function(styleProperty) {
...
},
...
};
var EventEmitter = {
addEventListener: function(eventName, func) {
},
...
};
С помощью mixins пример компонента выглядит следующим образом:
var Button = React.createClass({
mixins = [EventEmitter, Styleable],
...
});
С помощью composition я попытался создать компоненты для Styleable и EventEmitter следующим образом:
var StylableComponent = React.createClass({
addStyleProperty: ...
render: function() {
React.createElement(this.props.wrappedComponent, ...)??
}
}
var EventEmitterComponent = ...
Но я не знаю, как правильно их использовать. Я читал, что я должен использовать эти компоненты в качестве оболочки, но я не знаю, как этого добиться. Я попытался сделать это, как в функции рендеринга в приведенном выше примере. Как я могу создать экземпляр кнопки, которая имеет функции, аналогичные варианту mixin? Так что я просто передаю требуемые функции следующим образом:
<Button is={[StyleableComponent, EventEmitterComponent]}/>
Или я ожидаю неправильного поведения от композиции?
Ответ №1:
По сути, вы хотите создать функцию, которая возвращает совершенно новый компонент.
Например, для вашего EventEmitter это будет выглядеть примерно так:
import eventEmitter from 'your-event-emitter-location';
// This function takes a component as an argument, and returns a component.
function eventEmitterWrapper(ComposedComponent) {
// This is the brand new "wrapper" component we're creating:
return class extends Component {
render() {
// We want to return the supplied component, with all of its supplied
// props, but we also want to "add in" our additional behaviour.
return (
<ComposedComponent
{...this.props}
eventEmitter={eventEmitter}
/>
);
}
}
}
Чтобы использовать его, вы должны сделать что-то вроде этого:
import Button from '../Button';
import eventEmitterWrapper from '../../HOCs/event-emitter';
class Home extends Component {
render() {
const EventEmitterButton = eventEmitterWrapper(Button);
return (
<div>
<EventEmitterButton onClick={doSomething}>
Hello there!
</EventEmitterButton>
</div>
)
}
}
Наконец, внутри вашей кнопки у вас будет доступ к источнику событий из реквизита:
const Button = ({ children, onClick, eventEmitter }) => {
// Let's say, for example, you want to emit an event whenever the
// button is clicked. You could do something like this:
const clickHandler = (ev) => {
onClick(ev);
if (eventEmitter) {
eventEmitter.emit('click', ev);
}
};
return (
<button onClick={clickHandler}>
{children}
<button>
)
}
ПРИМЕЧАНИЕ: Это надуманный пример. Этот шаблон для меня немного вонючий; Button
Слишком много знает о внутренней работе источника событий (например, ему не нужно знать, что у него есть emit
метод. Если вы измените способ работы вашего источника событий, у вас будет куча «тупых» компонентов для обновления).
Чтобы исправить этот запах, мне нужно было бы узнать больше о вашем варианте использования, но я бы, скорее всего, заставил класс, возвращаемый eventEmitterWrapper
функцией, раскрывать простые специфические методы (такие как «emitEvent», который будет принимать один аргумент события мыши / клавиатуры)
Надеюсь, это иллюстрирует, как использовать компоненты более высокого порядка!
Дополнительная информация:
Комментарии:
1. Я прочитал приведенные источники, поэтому я попытался его реорганизовать. Это работает просто отлично, если есть одна функциональность, которую я хочу добавить, например, EventEmitter. Но если я хочу также добавить стилизуемый, у меня есть подобная цепочка
styleableWrapper
, которая затем принимает уже завернутый компонент, который для меня вонючий. Я надеюсь, вы поняли суть. У меня также больше нет доступа к функциям, объявленным в.eventEmitterWrapper
2. Компоненты более высокого порядка могут обертывать компоненты более высокого порядка; это совсем не вонючий (это просто композиция функций!). Я думаю, пока нет взаимозависимостей… это становится немного вонючим, если
styleableWrapper
зависит отeventEmitterWrapper
, но похоже, что это не так.3. Для вашего комментария о том, что у вас нет доступа к функциям, объявленным в
eventEmitterWrapper
, они должны быть переданы как реквизиты. Поскольку каждый HOC передает все делегированные реквизиты вниз, все, что вы передаете, будет собрано{...this.props}
.