Реагировать вкладку + в jQuery скроллер


Простой вкладке скроллер построен с React.js которая использует jQuery для анимации.

Вопросы

  1. Я только начал учиться реагировать и я недавно наткнулся <MyComponent></MyComponent>хотя информация ограничена. Где я могу пойти, чтобы узнать больше об этом?
  2. Было бы лучше использовать как предыдущий вопрос разрабатывать эту программу? Кажется, если я правильно понимаю, что я по сути делаю то же самое, но так, как я делал это не для настоящих масштабируемость и не правильный способ, чтобы выполнить эту задачу.

Я создал JSFiddle демо

/**
* Name:        React + jQuery Tab Scroller
* Description: A simple Tab Scroller
* @package     Chimera Apps
* @version     1.0.7
* @author      Chimera.Zen
* @copyright   Copyright (c) 2018, Chimera.Zen
* @link        https://github.com/ChimeraZen
* @license     http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/

/*
To be added: 
  - a means of getting the width of the tab list
  - enclose the list in a container and get displayed width
  - disable TabNav arrow if at start/end of tab list scrollable area
*/

const tabs = [
        {
          id: 0,
          label: "Archery",
          content: "Lorem Ipsum 1"
        },
        {
          id: 1,
          label: "Baseball",
          content: "Lorem Ipsum 2"
        },
        {
          id: 2,
          label: "Basketball",
          content: "Lorem Ipsum 3"
        },
        {
          id: 3,
          label: "Boxing",
          content: "Lorem Ipsum 4"
        },
        {
          id: 4,
          label: "Football",
          content: "Lorem Ipsum 5"
        },
        {
          id: 5,
          label: "Golf",
          content: "Lorem Ipsum 6"
        },
        {
          id: 6,
          label: "Soccer",
          content: "Lorem Ipsum 7"
        },
        {
          id: 7,
          label: "Surfing",
          content: "Lorem Ipsum 8"
        }
      ];

function TabContent(props) {
  return (
    <div className="tabContent">
      {props.content}
    </div>
  );
}

class Tab extends React.Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick(el) {
    this.props.handleClick(el.target)
  }

  render() {
    let active = (this.props.id === this.props.activeTab) ? "active" : ""
    return (
      <li id={this.props.id} onClick={this.onClick} className={active}>
        {this.props.label}
      </li>
    );
  }
}

class TabList extends React.Component {
  constructor(props) {
    super(props);
  }

  componentDidUpdate() {
    $(this.refs.tabList).animate({scrollLeft: this.props.scrollPosition}, 400)
  }

  render() {
    let tabList = this.props.tabs.map((tab) => {
      return (
        <Tab 
          key={tab.id}
          id={tab.id}
          activeTab={this.props.activeTab}
          label={tab.label}
          handleClick={this.props.handleClick}
        />
      );
    });
    return (
      <ul className="tabList" ref="tabList">
        {tabList}
      </ul>
    );
  }
}

class TabNav extends React.Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick(el) {
    this.props.handleClick(el.target)
  }

  render() {
    return (
      <div className="tabNav">
        <i className="fa fa-chevron-left prev" onClick={this.onClick}></i>
        <i className="fa fa-chevron-right next" onClick={this.onClick}></i>
      </div>
    );
  }
}

class TabScroller extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tabListWidth:  556, // Get TabList width, TabList's total width
      scrollDistance:  100, // Distance TabList should be scrolled
      scrollPosition:  0,
      scrollSpeed:      400, // Transition speed (Time in ms)
      activeTab:        0,  // ID of active Tab
      tabs:             tabs // Array of objects {id, label, content}
    }
    this.handleNavClick = this.handleNavClick.bind(this)
    this.handleTabClick = this.handleTabClick.bind(this)
  }

  handleNavClick(el) {
    let scrollPosition = this.state.scrollPosition
    let scrollRemaining = this.state.tabListWidth - this.state.scrollPosition

    if ($(el).hasClass("next")) {
      if (scrollRemaining < this.state.scrollDistance) {
        scrollPosition = scrollPosition + scrollRemaining
      } else {
        scrollPosition = scrollPosition + this.state.scrollDistance
      }
    } else {
      if (scrollPosition - this.state.scrollDistance < this.state.scrollDistance) {
        scrollPosition = 0
      } else {
        scrollPosition = scrollPosition - this.state.scrollDistance
      }
    }
    this.setState({scrollPosition: scrollPosition})
  }

  handleTabClick(el) {
    let tabId = parseInt(el.id)
    this.setState({activeTab: tabId})
  }

  render() {
    return (
      <div className="tabScroller">
        <div className="NavList">
          <TabNav handleClick={this.handleNavClick} />
          <TabList 
            tabs={this.state.tabs} 
            activeTab={this.state.activeTab}
            scrollPosition={this.state.scrollPosition} 
            handleClick={this.handleTabClick}
          />
        </div>
        <TabContent content={this.state.tabs[this.state.activeTab].content} />
      </div>
    );
  }
}

// ========================================

ReactDOM.render(
  <TabScroller />,
  document.getElementById('root')
);
* {
  margin: 0;
  padding: 0;
}

#root {
  background: #20262e;
  color: white;
}

.tabScroller {
  display: flex;
  flex-wrap: wrap;
}

.NavList {
  display: flex;
  overflow: hidden;
}

.tabNav {
  display: flex;
  justify-content: center;
  align-items: center;
}

.tabNav i {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 35px;
  height: 35px;
  cursor: pointer;
}

.tabNav i:hover {
  background: #353f4c;
}

ul {
  display: flex;
  align-items: stretch;
  width: 100%;
  list-style-type: none;
  border-left: 1.5px solid #2D333B;
  border-right: 1.5px solid #2D333B;
  overflow: hidden;
}

li {
  display: flex;
  align-items: center;
  padding: 0 10px;
  cursor: pointer;
  transition: all .3s ease-in-out;
}

ul li:last-child {
  margin: 0;
  border-right: none;
}

ul li:hover {
  background: #1566b4;
  transition: all .3s ease-in-out;
}

.active {
  background: #104C86;
}

.tabContent {
  display: flex;
  width: 100%;
  height: 300px;
  padding: 10px;
  border-top: 1.5px solid #2D333B;
}
<html>
  <body>
    <div id="root"></div>
  </body>
</html>



126
0
задан 26 февраля 2018 в 12:02 Источник Поделиться
Комментарии
1 ответ

Первый Вопрос


Я только начал учиться реагировать и я недавно наткнулся <MyComponent></MyComponent>хотя информация ограничена. Где я могу пойти, чтобы узнать больше об этом?

Вы уже смотрите на реакцию документации для компонента? Если нет, то я хотел бы начать там. В противном случае, не стесняйтесь искать в интернете "реагировать компонент" и аналогичные запросы.

Отзывы

Бесполезно конструктор переопределить

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


constructor(props) {
super(props);
}

Используя const против let

Для значений, которые не переназначаются после использования, const может быть использован вместо let. Например, в render метод Tabзначение active не переназначены - это просто используется в инструкции return сразу после назначения. Некоторые разработчики по умолчанию с использованием const если повторное назначение является обязательным

render() {
//this value doesn't get re-assigned, so could use const
let active = (this.props.id === this.props.activeTab) ? "active" : ""
return (
<li id={this.props.id} onClick={this.onClick} className={active}>
{this.props.label}
</li>
);
}

parseInt() без радикса

Нажмите кнопку обработчик вызовов parseInt() без второго параметра (т. е. основание). В МДН документации говорится, нужно "всегда указывать этот параметр , чтобы исключить ошибки считывания и гарантировать предсказуемое поведение"3. Так что звать надо 10 добавил в качестве второго параметра:

handleTabClick(el) {
let tabId = parseInt(el.id, 10)

Используя {tab.id} в качестве идентификатора атрибута

Этот идентификатор атрибута должно быть уникальным для всей страницы, потому что он "определяет уникальный идентификатор (ID), который должен быть уникальным во всем документе"1. Рассмотрим сценарий, в котором у вас есть еще один компонент, который выводит список товаров на одной странице с сча вкладок. Если этот компонент также используется идентификатор собственность каждого элемента списка, в котором оказалось целое, как это на вкладке элементы, то там может быть несколько элементов с одинаковым значением этого атрибута.

Кроме того, спецификация HTML 4.01 заявил, что идентификатор значение "должно начинаться с буквы ([А-Яа-я]) и может следовать любое количество букв, цифр ([0-9]), дефисов ("-"), символов подчеркивания ("_"), двоеточий ( " :") и точки (".")"2. Хотя большинство ограничений было снято с спецификацией HTML 5, он по-прежнему мудр, придерживаться, что для совместимости.

HTML-элемента данных атрибуты могут быть использованы здесь для того, чтобы определить tab.id значение и имеют идентификатор атрибута начать со строкой. Например:

render() {
//this value doesn't get re-assigned, so could use const
let active = (this.props.id === this.props.activeTab) ? "active" : ""
return (
<li id={'tab_' + this.props.id} data-tab-id={this.props.id} onClick={this.onClick} className={active}>
{this.props.label}
</li>
);
}

Затем нажмите кнопку обработчик метод можно использовать в jQuery .data() способ извлечения этого значения:

handleTabClick(el) {
let tabId = parseInt($(el).data('tabId'), 10);
this.setState({activeTab: tabId})
}

Событий делегата вместо того, чтобы использовать обработчики Click в качестве свойств

Вместо передачи нажмите кнопку обработчики для каждого компонента, один клик обработчик может быть использован на компонент TabScroller. Затем в обработчик щелчка, проверьте, если цель мероприятия совпадает с элементом tabnav, его компонент или в tablist компонента (с использованием .is()), и вызвать соответствующий метод:

onClick(event) {
const target = $(event.target);
if (target.is(".tabNav i")) {
this.handleNavClick(target);
}
else if (target.is('.tabList li')) {
this.handleTabClick(target);
}
}

Это позволит удалить некоторые из этих пустых конструктор переопределяет. Увидеть это в демонстрационном фрагменте ниже:



const tabs = [
{
id: 0,
label: "Archery",
content: "Lorem Ipsum 1"
},
{
id: 1,
label: "Baseball",
content: "Lorem Ipsum 2"
},
{
id: 2,
label: "Basketball",
content: "Lorem Ipsum 3"
},
{
id: 3,
label: "Boxing",
content: "Lorem Ipsum 4"
},
{
id: 4,
label: "Football",
content: "Lorem Ipsum 5"
},
{
id: 5,
label: "Golf",
content: "Lorem Ipsum 6"
},
{
id: 6,
label: "Soccer",
content: "Lorem Ipsum 7"
},
{
id: 7,
label: "Surfing",
content: "Lorem Ipsum 8"
}
];

function TabContent(props) {
return (
<div className="tabContent">
{props.content}
</div>
);
}

class Tab extends React.Component {

render() {
const active = (this.props.id === this.props.activeTab) ? "active" : ""
return (
<li id={'tab_' + this.props.id} data-tab-id={this.props.id} className={active}>
{this.props.label}
</li>
);
}
}

class TabList extends React.Component {

componentDidUpdate() {
$(this.refs.tabList).animate({scrollLeft: this.props.scrollPosition}, 400)
}

render() {
let tabList = this.props.tabs.map((tab) => {
return (
<Tab
key={tab.id}
id={tab.id}
activeTab={this.props.activeTab}
label={tab.label}
/>
);
});
return (
<ul className="tabList" ref="tabList">
{tabList}
</ul>
);
}
}

class TabNav extends React.Component {
render() {
return (
<div className="tabNav">
<i className="fa fa-chevron-left prev"></i>
<i className="fa fa-chevron-right next"></i>
</div>
);
}
}

class TabScroller extends React.Component {
constructor(props) {
super(props);
this.state = {
tabListWidth: 556, // Get TabList width, TabList's total width
scrollDistance: 100, // Distance TabList should be scrolled
scrollPosition: 0,
scrollSpeed: 400, // Transition speed (Time in ms)
activeTab: 0, // ID of active Tab
tabs: tabs // Array of objects {id, label, content}
}
this.handleNavClick = this.handleNavClick.bind(this)
this.handleTabClick = this.handleTabClick.bind(this)
}
onClick(event) {
const target = $(event.target);
if (target.is(".tabNav i")) {
this.handleNavClick(target);
}
else if (target.is('.tabList li')) {
this.handleTabClick(target);
}
}
handleNavClick(el) {
let scrollPosition = this.state.scrollPosition
let scrollRemaining = this.state.tabListWidth - this.state.scrollPosition

if ($(el).hasClass("next")) {
if (scrollRemaining < this.state.scrollDistance) {
scrollPosition = scrollPosition + scrollRemaining
} else {
scrollPosition = scrollPosition + this.state.scrollDistance
}
} else {
if (scrollPosition - this.state.scrollDistance < this.state.scrollDistance) {
scrollPosition = 0
} else {
scrollPosition = scrollPosition - this.state.scrollDistance
}
}
this.setState({scrollPosition: scrollPosition})
}

handleTabClick(el) {
let tabId = parseInt($(el).data('tabId'), 10);
this.setState({activeTab: tabId})
}

render() {
return (
<div className="tabScroller" onClick={this.onClick.bind(this)}>
<div className="NavList">
<TabNav />
<TabList
tabs={this.state.tabs}
activeTab={this.state.activeTab}
scrollPosition={this.state.scrollPosition}
/>
</div>
<TabContent content={this.state.tabs[this.state.activeTab].content} />
</div>
);
}
}

// ========================================

ReactDOM.render(
<TabScroller />,
document.getElementById('root')
);


* {
margin: 0;
padding: 0;
}

#root {
background: #20262e;
color: white;
}
.tabScroller,
.NavList,
.tabNav,
.tabNav i,
ul,
li,
.tabContent {
display: flex;
}
.tabScroller {
flex-wrap: wrap;
}

.NavList {
overflow: hidden;
}

.tabNav {
justify-content: center;
align-items: center;
}

.tabNav i {
justify-content: center;
align-items: center;
width: 35px;
height: 35px;
cursor: pointer;
}

.tabNav i:hover {
background: #353f4c;
}

ul {
align-items: stretch;
width: 100%;
list-style-type: none;
border-left: 1.5px solid #2D333B;
border-right: 1.5px solid #2D333B;
overflow: hidden;
}

li {
align-items: center;
padding: 0 10px;
cursor: pointer;
transition: all .3s ease-in-out;
}

ul li:last-child {
margin: 0;
border-right: none;
}

ul li:hover {
background: #1566b4;
transition: all .3s ease-in-out;
}

.active {
background: #104C86;
}

.tabContent {
width: 100%;
height: 300px;
padding: 10px;
border-top: 1.5px solid #2D333B;
}


<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>



1
ответ дан 27 февраля 2018 в 04:02 Источник Поделиться