Элементы интерфейса на React. Выпуск №1. Табы.

Элементы интерфейса на React. Выпуск №1. Табы.


В этом уроке мы реализуем простой и гибкий компонент вкладок, позволяющий отобразить абсолютно любой контент внутри.



Добавить наши вкладки в приложение будет очень просто:

<Tabs>
  <Tab title="Architects 😍">
     <p>Британская металкор/маткор группа из&nbsp;Брайтона. Группа известна своим агрессивным и&nbsp;эмоциональным вокалом, переплетающимся с&nbsp;динамичными гитарными партиями.</p>
  </Tab>
  <Tab title="Crystal Castles 😜">
     <p>Канадская группа, исполняющая экспериментальную электронную    музыку.</p>
  </Tab>
  <Tab title="Washed out 😢">       
     <p>Эрнест Грин&nbsp;&mdash; американский музыкант и&nbsp;продюсер, более известный под псевдонимом Washed Out.</p>
  </Tab>
</Tabs>



Компонент <Tabs />

Он содержит состояние и знает, какая вкладка отображается в данный момент, обрабатывает изменения для отображения / скрытия других вкладок.

export default class Tabs extends PureComponent {
   constructor(props) {
      super(props);
  
      this.state = {
         // Индекс выбранной вкладки
         selectedTabIndex: 0,
      };
   }

   getAdditionalProps = (index, props) => ({
      handleClick: this.handleTabClick,
      tabIndex: index,
      selected: index === this.state.selectedTabIndex,
      ...props,
   });

   /**
   * Клонируем children(компоненты <Tab />),
   * добавляя в каждый нужные нам props:
   * - обработчик события клика;
   * - индекс вкладки
   * - флаг активности.
   */
   getChildrenTabsWithProps = () => {
      return React.Children.map(this.props.children, (child, index) =>
        React.cloneElement(
          child, 
          this.getAdditionalProps(index, child.props)
        ),
      );
   };

   /**
   * Рендерим контент активной вкладки
   */
   getActiveTabContent = () => {
      const { children } = this.props;
      const { selectedTabIndex } = this.state;
      const currentChildren = children[selectedTabIndex];
    
      if (currentChildren) {
       return currentChildren.props.children;
      }
    
      return false;
   };

   /**
   * Переключаем вкладки
   */
   handleTabClick = tabIndex => {
      this.setState({ selectedTabIndex: tabIndex });
   };

   render() {
      const childrenTabsWithProps = this.getChildrenTabsWithProps();
      const tabContent = this.getActiveTabContent();
    
      return (
         <div className="Tabs">
            <ul className="Tabs__list">{childrenTabsWithProps}</ul>
            <div className="Tabs__content">{tabContent}</div>
         </div>
      );
   }
}



Компонент <Tab />

Его цель — просто отобразить вкладку, позволить указать заголовок и вызвать функцию родителя (компонент <Tabs/>) по событию onClick, передав в нее свой индекс.

export default class Tab extends PureComponent {
  handleClick = () => {
    // Вызываем родительский метод для переключения вкладки
    this.props.handleClick(this.props.tabIndex);
  };

  render() {
    return (
      <li
        className={`Tabs__item ${this.props.selected
          ? 'Tabs__item--selected'
          : ''}`}
        onClick={this.handleClick}
      >
        {this.props.title}
      </li>
    );
  }
}



Вывод

Нам удалось реализовать простой компонент вкладок на React с малым количеством кода, который легко поддерживать и улучшать.

Работу компонентов и весь код вы можете посмотреть в интерактивном демо. Также, в качестве дополнительной практики, можно реализовать возможность указывать активную вкладку по умолчанию.

Все ваши вопросы и замечания вы можете писать на ask@deep.digital.


Полезные ссылки

React.Children

React.cloneElement()

codesandbox.io - онлайн-редактор для React, Preact, Vue.

StackBlitz - Online VS Code IDE for Angular & React.

Report Page