Предпочитаете чистую функцию, чем 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 в чистом виде, а просто улучшить его. В этом случае, что бы вы посоветовали сделать это?

Большое спасибо за вашу помощь!

0 ответов

Другие вопросы по тегам