React 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组件中使用state时,该组件被称为有状态的 。 React组件可以使用state属性定义自己的状态,以处理有状态的组件,例如我们的音乐播放器。

当用户单击播放暂停下一个上一个按钮以及播放器中的曲目时,我们的组件将更新其当前状态。

道具与状态

对于React应用程序,了解propstate之间的区别很重要。 我们的音乐播放器有两个state变量,这些变量确定在给定时间点显示应用程序的方式。 App组件是驱动子组件显示的主要组件,即Controls组件和TrackList组件。 为了使这两个组件能够接收有关我们应用程序状态的信息, App组件会将信息作为道具传递给子组件。 这些道具然后可以在子组件中使用,以正确显示其应用程序片段。 要了解的另一件重要事情是,每次我们的App组件更新时,我们的Controls组件和TrackList组件也会被更新,因为它们依赖于App组件中的信息。

控制项

我们的Controls组件是我们App组件的第一个子组件。 Controls组件具有两个道具: onClickplayingonClick属性允许我们将在App组件中定义的handleClick函数传递给Controls组件。 当用户单击“ Controls组件中的按钮之一时,将调用handleClick函数。 playing道具可以使Controls组件知道播放器的当前状态,因此我们可以正确渲染播放图标或暂停图标。

让我们探讨如何在Controls组件中呈现按钮和处理单击。

Controls组件中,我们有三个重要的按钮:

  1. << (上一个)按钮(指向左侧的箭头图标),用于选择列表中的上一个曲目
  2. 播放/暂停按钮可播放和暂停音乐
  3. >> (下一个)按钮-指向右侧的箭头图标-用于选择列表中的下一首曲目。

当单击这些按钮中的每个按钮时,我们将调用从App组件传入的单击处理程序函数。 音乐播放器应用程序中的每个按钮都有一个id ,该id将有助于我们确定应如何处理特定的点击。

handleClick函数的内部,我们使用switch语句,该语句使用被单击的按钮的id (即e.target.id确定如何处理按钮的操作。 在下一节中,我们将看看switch语句在每种情况下会发生什么。

播放按钮

单击播放按钮后,我们需要更新应用程序的一些部分。 我们需要将播放图标切换为暂停图标。 如果currentTrackIndex当前设置为0,我们还需要对其进行更新。为了更改应用程序的这两部分,我们将调用setState ,该函数可用于每个React组件。

setState函数可用于所有React组件,这是我们更新音乐播放器状态的方式。 setState函数中的第一个参数可以是对象或函数。 如果我们不依赖应用程序的当前状态来计算下一个状态,则使用对象作为第一个参数是一种很好的方法,它看起来像这样: this.setState({currentState:'newState'}) 。 在我们的例子中,我们依靠应用程序的当前状态来确定应用程序的下一个状态,因此我们将要使用一个函数。 React文档指出了为什么这很重要:

React可以将多个setState()调用批处理到单个更新中以提高性能。 由于this.propsthis.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函数完成后调用的回调函数。 当单击播放按钮,并且我们的应用程序状态已更新时,我们要开始播放音乐。 我们传入this.playAudio函数作为setState函数的第二个参数。

},this.playAudio);

调用playAudio按钮时,我们引用audio标签,并通过Web Audio API调用我们可用的loadplay功能。

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

ref DOM元素

为了引用实际的音频DOM元素来播放音频,我们需要使用可用于所有React组件的特殊属性ref属性。 从React文档中:

在HTML元素上使用ref属性时, 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函数调用的第二个参数是this.pauseAudio函数,它引用audio元素并调用pause()函数。

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

<<(上一个)按钮

当单击<< <<图标时,上一个按钮的id与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);

setState返回null

React 16中的新变化之一是,当我们从setState函数返回null时,将不会重新渲染我们的应用程序。 我们的曲目列表有11个可用曲目。 如果用户继续单击<<按钮,则currentTrackIndex会递减直到变为0。一旦变为0,我们就不再希望递减currentTrackIndex并且不再需要重新渲染应用程序。 单击>>图标时,我们也将执行同样的操作。 如果currentTrackIndex等于(或大于)列表(11)中的轨道数,则从setState返回null

>> (下一个)按钮

调用>>按钮时,我们具有与<<按钮类似的功能。 每次用户单击>> ,我们都会增加currentTrackIndex并检查currentTrackIndex是否不大于曲目列表的长度。 如果是,则在setState函数调用中返回null

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组件的状态包含一个变量,即tracks变量。

Lifecyle方法componentDidMountcomponentDidUpdate

除了setState函数之外,每个React组件还具有可用的生命周期方法。 我们的TrackList组件使用其中两个, componentDidMountcomponentDidUpdatecomponentDidMount当阵营组件可在DOM被调用。 在这种情况下,我们想向组件中添加一些数据,因此在componentDidMount调用setState是合适的时间。

当我们的App组件更新currentTrackIndex ,会触发TrackList组件中的componentDidUpdate方法,因为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;
    }
  }
}

显示曲目列表

我们使用JavaScript map函数遍历轨道数组,并为数组中的每个元素调用一个函数。 我们调用的函数是renderListItem ,它包含一些逻辑来确定currentTrackIndex是否是我们正在渲染的数组中的当前元素。 如果是的话,我们需要确保li上的className值包括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是当前选定的音轨,请确保li附加了selected类别。

  • ref :我们使用ref属性来计算轨道列表容器的正确位置。 如果当前轨迹不可见,我们将计算当前轨迹的位置并使它可见。 我们需要访问实际的DOM元素才能正确进行此计算。

  • onClick :当用户选择特定曲目时,我们调用此函数,该函数调用this.props.selectTrackNumber 。 该函数从父App组件传递到TrackList组件,就像Controls组件的单击处理程序一样。 调用此函数时,我们的应用程序状态将更新,同时currentTrackIndex设置为用户选择的曲目号。

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

试试看!

查看Codepen示例。 专辑封面来自一个叫做Glass Animals的乐队的专辑。 由于我们无法合法地播放“玻璃动物”原声带,因此,我们选择了一些免版税的音乐来代替它播放,因此我们可以充分发挥音乐播放器的作用。

见笔阵营DailyUI - 009 -音乐播放器由杰克·奥利弗( @jackoliver )上CodePen

此帖子是Fullstack React的React Daily UI帖子系列的一部分,该系列是Jack OliverSophia Shoemaker和Fullstack React团队的其他成员共同努力的结果。

是否想深入研究React基础知识? 查看Fullstack React:ReactJS和Friends完整指南以了解更多信息。

From: https://www.sitepoint.com/react-16-new-features/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值