Предпочитаете чистую функцию, чем React Component?
Я работаю над видеоплеером HTMl5 для французской компании. Мы используем React и Redux для создания пользовательского интерфейса, и он работает очень хорошо, очень приятно кодировать! В настоящее время мы используем eslint-plugin-Reaction для проверки стиля кода React. Начиная с последних версий, Линтер рекомендует использовать чистые функции вместо React Components (см. Правило), но это вызывает некоторые споры в моей команде.
Мы уже используем чистую функцию для очень маленьких компонентов, которые рендерит всегда одни и те же вещи (для данного реквизита). С этим проблем нет. Но для больших компонентов, на мой взгляд, чистые функции делают код менее элегантным (и менее единообразным по сравнению с другими компонентами).
Это пример одного из наших компонентов, который следует изменить:
const ControlBar = ({ actions, core, root }) => {
const onFullscreenScreen = (isFullscreen) => {
const playerRoot = root;
if (isFullscreen && !screenfull.isFullscreen) {
screenfull.request(playerRoot);
} else {
screenfull.exit();
}
};
const renderIconButton = (glyph, action, label = false) => {
let content = (
<Button modifier="icon" clickCallback={ action }>
<Icon glyph={ glyph } />
</Button>
);
if (label) {
content = <TooltipOrigin content={label}>{content}</TooltipOrigin>;
}
return content;
};
const renderPlayButton = () => {
const { play, pause } = actions;
const { playerState } = core;
if (playerState === CoreStates.PAUSED) {
return renderIconButton(playGlyph, play, 'lecture');
}
return renderIconButton(pauseGlyph, pause, 'pause');
};
const renderMuteButton = () => {
const { mute, unmute } = actions;
const { currentVolume } = core;
if (currentVolume === 0) {
return renderIconButton(muteGlyph, unmute);
}
return renderIconButton(volumeGlyph, mute);
};
const renderFullscreenButton = () => {
const { isFullscreen } = core;
if (!isFullscreen) {
return renderIconButton(fullscreenGlyph, () => { onFullscreenScreen(true); });
}
return renderIconButton(fullscreenExitGlyph, () => { onFullscreenScreen(false); });
};
const { setCurrentVolume } = actions;
const { currentVolume } = core;
return (
<div className={ style.ControlBar }>
<div className={ style.audio }>
{ renderMuteButton() }
<SoundBar setCurrentVolume={ setCurrentVolume } volume={ currentVolume } />
</div>
<div className={ style.controls }>
{ renderPlayButton() }
</div>
<div className={ style.settings }>
{ renderFullscreenButton() }
</div>
</div>
);
};
ControlBar.propTypes = {
actions: PropTypes.object.isRequired,
core: PropTypes.object.isRequired,
root: PropTypes.object.isRequired,
};
export default ControlBar;
против:
export default class ControlBar extends Component {
static propTypes = {
actions: PropTypes.object.isRequired,
core: PropTypes.object.isRequired,
root: PropTypes.object.isRequired,
};
onFullscreenScreen(isFullscreen) {
const playerRoot = this.props.root;
if (isFullscreen && !screenfull.isFullscreen) {
screenfull.request(playerRoot);
} else {
screenfull.exit();
}
}
renderIconButton(glyph, action, label = false) {
let content = (
<Button modifier="icon" clickCallback={ action }>
<Icon glyph={ glyph } />
</Button>
);
if (label) {
content = <TooltipOrigin content={label}>{content}</TooltipOrigin>;
}
return content;
}
renderPlayButton() {
const { play, pause } = this.props.actions;
const { playerState } = this.props.core;
if (playerState === CoreStates.PAUSED) {
return this.renderIconButton(playGlyph, play, 'lecture');
}
return this.renderIconButton(pauseGlyph, pause, 'pause');
}
renderMuteButton() {
const { mute, unmute } = this.props.actions;
const { currentVolume } = this.props.core;
if (currentVolume === 0) {
return this.renderIconButton(muteGlyph, unmute);
}
return this.renderIconButton(volumeGlyph, mute);
}
renderFullscreenButton() {
const { isFullscreen } = this.props.core;
if (!isFullscreen) {
return this.renderIconButton(fullscreenGlyph, () => { this.onFullscreenScreen(true); });
}
return this.renderIconButton(fullscreenExitGlyph, () => { this.onFullscreenScreen(false); });
}
render() {
const { setCurrentVolume } = this.props.actions;
const { currentVolume } = this.props.core;
return (
<div className={ style.ControlBar }>
<div className={ style.audio }>
{ this.renderMuteButton() }
<SoundBar setCurrentVolume={ setCurrentVolume } volume={ currentVolume } />
</div>
<div className={ style.controls }>
{ this.renderPlayButton() }
</div>
<div className={ style.settings }>
{ this.renderFullscreenButton() }
</div>
</div>
);
}
}
Нам нравится структура React Component. PropTypes и реквизиты по умолчанию могут быть внутри класса благодаря ES7, это невозможно с чистыми функциями. И, в частности, в этом примере у нас есть много функций для визуализации подкомпонентов.
Мы могли бы просто отключить это правило, если нам не нравится, но мы действительно хотим понять это, и мы заботимся о производительности и реагируем на передовой опыт. Так что, может быть, вы можете помочь нам.
Как вы можете мне помочь? Я бы получил другие мнения по поводу этой интересной проблемы. Какие аргументы в пользу чистых функций? Возможно, решение состоит не в том, чтобы изменить компонент ControlBar в чистом виде, а просто улучшить его. В этом случае, что бы вы посоветовали сделать это?
Большое спасибо за вашу помощь!