Статьи

Что нового в Реакте 16?

В этом посте мы узнаем, как создать музыкальный проигрыватель, используя некоторые новые функции в React 16.

Внедряя этот музыкальный проигрыватель, мы узнаем о некоторых изменениях в React 16. Есть довольно много изменений, поэтому мы не будем охватывать все из них, но мы рассмотрим те, которые важны и которые Вы можете реализовать сегодня.

Полный источник этого поста доступен на GitHub .

Чтобы запустить приложение, загрузите код, cd в каталог проекта и введите:

 npm install npm start 

Состояние в приложении React

Все приложения React включают свойство, называемое state которое определяет, как и какие компоненты (и любые данные, связанные с этими компонентами) должны отображаться.

Наш музыкальный проигрыватель имеет свойство state которое содержит две важные части информации: одну переменную, которая указывает, воспроизводит ли проигрыватель музыку — playing логическое значение, — и одну переменную, которая отслеживает состояние текущей дорожки, — переменную currentTrackIndex .

 this.state = { playing: false, currentTrackIndex: 0 }; 

Что такое государство?

Когда мы ссылаемся на состояние компонента, мы имеем в виду снимок экземпляра компонента на странице.

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

Когда пользователь нажимает кнопки воспроизведения , паузы , перехода вперед и назад , а также дорожки в проигрывателе, наш компонент будет обновлять свое текущее состояние.

Реквизит против государства

Для приложений React важно понимать разницу между реквизитом и состоянием . Наш музыкальный проигрыватель имеет две переменные state которые определяют способ отображения нашего приложения в данный момент времени. Компонент App является нашим основным компонентом, который управляет отображением наших дочерних компонентов — компонента Controls компонента TrackList . Чтобы эти два компонента могли получать информацию о состоянии нашего приложения, компонент App будет передавать информацию в качестве реквизитов дочерним компонентам. Затем эти реквизиты можно использовать в дочернем компоненте для правильного отображения своих частей приложения. Еще одна важная вещь, которую нужно понять, это то, что каждый раз, когда обновляется наш компонент App , наши Controls компонент TrackList также будут обновляться, поскольку они полагаются на информацию из компонента App .

управления

Наш компонент Controls является первым дочерним элементом нашего компонента App . Компоненту Controls предоставляется два реквизита: onClick и onClick . Свойство onClick позволяет нам передавать нашу функцию handleClick мы определили в компоненте App компонент Controls . Когда пользователь нажимает одну из кнопок в нашем компоненте Controls , handleClick функция handleClick . playing опора позволяет компоненту Controls узнать текущее состояние проигрывателя, чтобы мы могли правильно отобразить значок воспроизведения или значок паузы .

Давайте рассмотрим, как мы визуализируем наши кнопки и обрабатываем щелчки в нашем компоненте Controls .

В нашем компоненте Controls у нас есть три важные кнопки:

  1. Кнопка << (предыдущая) — значок стрелки, указывающий влево — выбирает предыдущую дорожку в списке
  2. Кнопка воспроизведения / паузы , которая воспроизводит и приостанавливает музыку
  3. Кнопка >> (следующая) — значок стрелки, указывающий вправо — выбирает следующую дорожку в списке.

Когда нажимается каждая из этих кнопок, мы вызываем функцию обработчика щелчка, которую мы передали из компонента App . Каждая из кнопок в нашем приложении музыкального проигрывателя имеет id который поможет нам определить, как следует обрабатывать конкретный щелчок.

Во внутренних handleClick мы используем оператор switch который использует id нажатой кнопки — e.target.id чтобы определить, как обрабатывать действие от кнопки. В следующем разделе мы рассмотрим, что происходит в каждом случае оператора switch.

Кнопка воспроизведения

Когда кнопка воспроизведения нажата, нам нужно обновить несколько частей нашего приложения. Нам нужно переключить значок воспроизведения на значок паузы. Нам также необходимо обновить currentTrackIndex если он в настоящее время установлен на 0. Чтобы изменить эти две части нашего приложения, мы вызовем setState , функцию, доступную для каждого компонента React.

Функция setState доступна для всех компонентов React, и именно так мы обновляем состояние нашего музыкального проигрывателя. Первый аргумент в функции setState может быть либо объектом, либо функцией. Если мы не полагаемся на текущее состояние приложения для вычисления следующего состояния, использование объекта в качестве первого аргумента является идеальным подходом и выглядит следующим образом: this.setState({currentState:'newState'}) . В нашем случае мы полагаемся на текущее состояние приложения, чтобы определить следующее состояние нашего приложения, поэтому мы хотим использовать функцию. Документация React указывает, почему это важно:

React может setState() несколько setState() в одно обновление для повышения производительности. Поскольку this.props и this.state могут обновляться асинхронно, вы не должны полагаться на их значения для вычисления следующего состояния.

Поскольку React 16 включает больше своих функций (включая асинхронный рендеринг), это различие станет более важным для понимания.

Когда кнопка воспроизведения нажата и мы вызываем setState , мы передаем функцию, потому что мы полагаемся на текущее значение переменной состояния currentTrackIndex . Первый аргумент, который передается в функцию, — это предыдущее состояние нашего приложения, а второй аргумент — текущий props . В нашем случае нам просто нужно предыдущее состояние приложения, чтобы определить следующее состояние:

 case "play": this.setState((state, props) => { let currentTrackIndex = state.currentTrackIndex; if (currentTrackIndex === 0) { currentTrackIndex = 1; } 

Как только мы правильно установили currentTrackIndex на основе предыдущего значения currentTrackIndex , мы затем возвращаем объект значений, которые мы хотим обновить. В случае нажатия кнопки воспроизведения мы обновляем наше playing логическое значение на true и устанавливаем значение currentTrackIndex :

 return { playing: true, currentTrackIndex: currentTrackIndex }; 

Второй аргумент, который передается в функцию setState является функцией обратного вызова, которая setState после setState функции setState . Когда кнопка воспроизведения нажата, а состояние нашего приложения обновлено, мы хотим начать воспроизведение музыки. Мы this.playAudio функцию this.playAudio в качестве второго аргумента нашей функции setState .

 },this.playAudio); 

Когда playAudio кнопка playAudio , мы playAudio на audio метку и вызываем функции load и play доступные нам через API Web Audio .

 playAudio(){ this.audioElement.load(); this.audioElement.play(); } 

ref на элемент DOM

Чтобы ссылаться на фактический аудио-DOM-элемент для воспроизведения аудио, нам нужно использовать специальный атрибут, доступный для всех компонентов React, атрибут ref . Из документации React:

Когда атрибут ref используется в элементе HTML, обратный вызов ref получает базовый элемент DOM в качестве аргумента.

В нашей ситуации мы добавляем атрибут ref к нашему audio DOM-элементу, и это позволяет нам воспроизводить аудио для каждой дорожки:

 <audio ref={(audio)=>{this.audioElement = audio}} src={"/songs/"+this.state.currentTrackIndex+".mp3"}/> 

Кнопка паузы

Когда нажимается кнопка паузы , мы вызываем this.setState и устанавливаем для нашего playing логического значения значение false .

 case "pause": this.setState({ playing: false },this.pauseAudio); break; 

Вторым аргументом для нашего setState функции setState является наша функция this.pauseAudio , которая ссылается на audio элемент и вызывает функцию pause() .

 pauseAudio(){ this.audioElement.pause(); } 

Кнопка << (предыдущая)

При нажатии значка << id предыдущей кнопки соответствует регистру «prev» оператора switch, поэтому выполняется код, связанный с регистром «prev». В случае «prev» мы снова вызываем this.setState() с помощью функции, аналогичной той, которую мы использовали для воспроизведения и приостановки приложения. На этот раз мы используем предыдущее значение currentTrackIndex чтобы уменьшить значение и вернуть объект, чтобы установить currentTrackIndex в новое значение.

 case "prev": this.setState((state, props) => { let currentIndex = state.currentTrackIndex - 1; if (currentIndex <= 0) { return null; } else { return { playing:true,currentTrackIndex: currentIndex }; } },this.playAudio); 

Возвращение null из setState

Одно из новых изменений в React 16 заключается в том, что когда мы возвращаем значение null из функции setState , наше приложение не будет повторно отображаться. Наш трек-лист имеет 11 треков. Если пользователь продолжает нажимать кнопку << , currentTrackIndex будет уменьшаться до тех пор, пока не достигнет 0. Как только он достигнет 0, мы больше не хотим уменьшать currentTrackIndex и нам больше не нужно повторно отображать наше приложение. Мы также делаем то же самое, когда щелкает наш значок >> . Если currentTrackIndex равно (или больше) количеству дорожек в нашем списке (11), мы возвращаем null из setState .

Кнопка >> (следующая)

Когда кнопка >> вызывается, мы имеем такую ​​же функциональность, как и кнопка << . Каждый раз, когда пользователь нажимает >> , мы увеличиваем значение currentTrackIndex и проверяем, что currentTrackIndex не превышает длину списка дорожек. Если это так, мы возвращаем null в нашем setState функции setState .

 case "next": this.setState((state, props) => { let currentIndex = state.currentTrackIndex + 1; if (currentIndex > data.tracks.length) { return null; } else { return { playing:true,currentTrackIndex: currentIndex }; } },this.playAudio); break; 

Список треков

Мы жестко закодировали данные трек-листа в файл JSON для простоты понимания концепций в этом посте. Мы импортируем данные из файла JSON вверху, и в нашем методе жизненного цикла componentDidMount мы устанавливаем состояние нашего компонента TrackList . Состояние нашего компонента TrackList содержит одну переменную, переменную TrackList .

Методы жизненного цикла componentDidMount и componentDidUpdate

Каждый компонент React, в дополнение к функции setState , также имеет доступные методы жизненного цикла. Наш компонент TrackList использует два из них: componentDidMount и componentDidUpdate . componentDidMount вызывается, когда компонент React доступен в DOM. В этом случае мы хотим добавить некоторые данные в наш компонент, так что для этого setState вызвать setState в componentDidMount .

Когда наш компонент App обновляет currentTrackIndex , метод componentDidUpdate в нашем компоненте TrackList запускается, потому что компонент TrackList получает новые данные. Когда компонент TrackList получает новые данные, мы хотим убедиться, что TrackList выбранная дорожка находится в нашем окне просмотра, поэтому мы делаем некоторые вычисления, чтобы определить, где в DOM существует текущая выбранная дорожка, и сделать ее отображаемой в представлении контейнера списка дорожек. ,

 componentDidUpdate() { if (this.activeTrack) { let topOfTrackList = this.trackList.scrollTop; let bottomOfTrackList = this.trackList.scrollTop + this.trackList.clientHeight; let positionOfSelected = this.activeTrack.offsetTop; if ( topOfTrackList > positionOfSelected || bottomOfTrackList < positionOfSelected ) { this.trackList.scrollTop = positionOfSelected; } } } 

Отображение списка треков

Мы используем функцию map JavaScript, чтобы зациклить наш массив дорожек и вызвать функцию для каждого элемента в массиве. Мы вызываем функцию renderListItem , которая содержит некоторую логику, чтобы определить, является ли currentTrackIndex текущим элементом в массиве, который мы отображаем. Если это так, нам нужно убедиться, что значение для className в li включает selected строку. Это гарантирует, что стилизация для выбранной дорожки будет отличаться по сравнению с остальной частью списка.

 renderListItem(track, i) { let trackClass = this.props.currentTrackIndex === track.id ? "selected" : ""; return ( <li key={track.id} className={trackClass} ref={cur => { if (this.props.currentTrackIndex === track.id) { this.activeTrack = cur; } }} onClick={()=>{this.props.selectTrackNumber(track.id)}} > <div className="number">{track.id}</div> <div className="title">{track.title}</div> <div className="duration">{track.duration}</div> </li> ); } 

Элемент li также содержит некоторые другие важные атрибуты:

  • key : всякий раз, когда у нас есть список, нам нужно включить этот атрибут, чтобы список отображался правильно. Для получения дополнительной информации об использовании ключей со списками в React, ознакомьтесь с этой статьей в документации React .

  • className : чтобы убедиться, что к li прикреплен selected класс, если это className выбранный трек.

  • ref : мы используем атрибут ref для вычисления правильного местоположения контейнера списка дорожек. Если текущий трек не виден, мы рассчитываем местоположение текущего трека и делаем его видимым. Нам нужно получить доступ к фактическому элементу DOM, чтобы сделать этот расчет правильно.

  • onClick : когда пользователь выбирает конкретную дорожку, мы вызываем эту функцию, которая вызывает this.props.selectTrackNumber . Эта функция передается в компонент TrackList из нашего родительского компонента App , как и обработчик TrackList компонента Controls . Когда эта функция вызывается, состояние нашего приложения обновляется, и currentTrackIndex получает номер дорожки, выбранный пользователем.

 selectTrackNumber(trackId){ this.setState({currentTrackIndex:trackId,playing:true},this.playAudio); } 

Попробуйте!

Посмотрите пример Codepen. Обложка альбома взята из альбома группы под названием Glass Animals. Поскольку мы не можем легально транслировать саундтрек «Glass Animals», мы выбрали музыку без роялти, чтобы играть вместо нее, чтобы мы могли получить полный эффект от музыкального проигрывателя.

Этот пост является частью серии постов React Daily UI в Fullstack React , совместными усилиями Джека Оливера , Софии Сапожник и остальной команды в Fullstack React.

Хотите глубже погрузиться в основы React? Проверьте Fullstack React: полное руководство по ReactJS & Friends, чтобы узнать больше.