关于React你应该了解的一切:开始构建前你所需的基础知识

wxl9521 7年前
   <p>摘要: 你是不是对React充满好奇,并且还没有机会学习它?或者你已经尝试过,却挣扎于核心概念的掌握?又或者你已经学习过基础,但是想巩固一下知识?无论你是以上哪种类型,这篇文章都适合你。我们要基于以下的全新React概念构建一个简单的React音乐播放器。</p>    <p><img src="https://simg.open-open.com/show/3fd16d59d7dbafa7fe386e3b5ba8ef13.png" alt="关于React你应该了解的一切:开始构建前你所需的基础知识 " width="550" height="263"></p>    <p><strong>本文由VicSusi在众成翻译平台翻译。</strong></p>    <p>你是不是对React充满好奇,并且还没有机会学习它?或者你已经尝试过,却挣扎于核心概念的掌握?又或者你已经学习过基础,但是想巩固一下知识?无论你是以上哪种类型,这篇文章都适合你。</p>    <p>我们要基于以下的全新React概念构建一个简单的React音乐播放器。</p>    <p>下面是我们要了解的内容:</p>    <ul>     <li>什么是React组件?</li>     <li>ReactDOM渲染</li>     <li>类 vs 函数化组件</li>     <li>JSX</li>     <li>状态</li>     <li>事件操作</li>     <li>异步设置状态</li>     <li>属性</li>     <li>文件系统</li>    </ul>    <p>以上是构建和维护一个React应用你需要了解的知识。但我们打算按部分来介绍。</p>    <h3>设置</h3>    <p>现在情况是这样:你要完成一个小小的任务。他们创建了用户上传音乐的页面,并用醒目的颜色让页面更加直观。但他们需要你来完成困难的部分-就是指让这个页面正常工作。</p>    <p>开始前,创建一个新的项目目录,添加 <a href="/misc/goto?guid=4959755865609009687" rel="nofollow,noindex">以下的三个文件</a> .</p>    <p>app.css</p>    <pre>  <code class="language-javascript">body {    <pre>  background: #f9f9f9;    font-family: 'Open Sans', sans-serif;    text-align: center;  }    #container {    position: relative;    z-index: 2;    padding-top: 100px;  }    .play {    display: block;    width: 0;    height: 0;    border-top: 50px solid transparent;    border-bottom: 50px solid transparent;    border-left: 60px solid #2c3e50;    margin: 100px auto 50px auto;    position: relative;    z-index: 1;    transition: all 0.3s;    -webkit-transition: all 0.3s;    -moz-transition: all 0.3s;    left: 10px;  }    .play:before {    content: '';    position: absolute;    top: -75px;    left: -115px;    bottom: -75px;    right: -35px;    border-radius: 50%;    border: 10px solid #2c3e50;    z-index: 2;    transition: all 0.3s;    -webkit-transition: all 0.3s;    -moz-transition: all 0.3s;  }  .play:after {    content: '';    opacity: 0;    transition: opacity 0.6s;    -webkit-transition: opacity 0.6s;    -moz-transition: opacity 0.6s;  }  .play:hover:before, .play:focus:before {    transform: scale(1.1);    -webkit-transform: scale(1.1);    -moz-transform: scale(1.1);  }  .play.active {    border-color: transparent;  }  .play.active:after {    content: '';    opacity: 1;    width: 25px;    height: 80px;    position: absolute;    right: 8px;    top: -40px;    border-right: 20px solid #2c3e50;    border-left: 20px solid #2c3e50;  }    h1 {    text-transform: uppercase;    color: #34495e;    letter-spacing: 2px;    font-size: 2em;    margin-bottom: 0;  }    canvas {    position: fixed;    left: 0;    top: 0;    width: 100%;    height: 100%;  }    audio {    position: fixed;    left: 10px;    bottom: 10px;    width: calc(100% - 20px);  }  </pre>    <div>```</div>    <div>app.js</div>    <div>```</div>    <div>    <pre>var ALPHA,    AudioAnalyser,    COLORS,    MP3_PATH,    NUM_BANDS,    NUM_PARTICLES,    Particle,    SCALE,    SIZE,    SMOOTHING,    SPEED,    SPIN,    TIMES_CALLED,    ANALYSER;    NUM_PARTICLES = 150;    NUM_BANDS = 128;    TIMES_CALLED = 0;    SMOOTHING = 0.5;    MP3_PATH = 'music.mp3';    SCALE = {    MIN: 5.0,    MAX: 80.0  };    SPEED = {    MIN: 0.2,    MAX: 1.0  };    ALPHA = {    MIN: 0.8,    MAX: 0.9  };    SPIN = {    MIN: 0.001,    MAX: 0.005  };    SIZE = {    MIN: 0.5,    MAX: 1.25  };    COLORS = [    '#69D2E7',    '#1B676B',    '#BEF202',    '#EBE54D',    '#00CDAC',    '#1693A5',    '#F9D423',    '#FF4E50',    '#E7204E',    '#0CCABA',    '#FF006F'  ];  function getAnimation(file) {    AudioAnalyser = (function() {      AudioAnalyser.AudioContext = self.AudioContext || self.webkitAudioContext;        AudioAnalyser.enabled = AudioAnalyser.AudioContext != null;        function AudioAnalyser(audio, numBands, smoothing) {        var src;        this.audio = audio != null ? audio : new Audio();        this.numBands = numBands != null ? numBands : 256;        this.smoothing = smoothing != null ? smoothing : 0.3;        this.audio = document.getElementById('audio');        if (!this.audio) {          return;        }        try {          this.audio.src = window.URL.createObjectURL(file);        } catch (err) {          console.log(err);        }        this.context = new AudioAnalyser.AudioContext();        this.jsNode = this.context.createScriptProcessor(2048, 1, 1);        this.analyser = this.context.createAnalyser();        this.analyser.smoothingTimeConstant = this.smoothing;        this.analyser.fftSize = this.numBands * 2;        this.bands = new Uint8Array(this.analyser.frequencyBinCount);        this.audio.addEventListener(          'play',          (function(_this) {            return function() {              if (TIMES_CALLED === 1) {                return;              }              ANALYSER.start();              TIMES_CALLED++;              _this.source = _this.context.createMediaElementSource(_this.audio);              _this.source.connect(_this.analyser);              _this.analyser.connect(_this.jsNode);              _this.jsNode.connect(_this.context.destination);              _this.source.connect(_this.context.destination);              return (_this.jsNode.onaudioprocess = function() {                _this.analyser.getByteFrequencyData(_this.bands);                if (!_this.audio.paused) {                  return typeof _this.onUpdate === 'function'                    ? _this.onUpdate(_this.bands)                    : void 0;                }              });            };          })(this)        );      }        AudioAnalyser.prototype.start = function() {        return this.audio.play();      };        AudioAnalyser.prototype.stop = function() {        return this.audio.pause();      };        return AudioAnalyser;    })();      Particle = (function() {      function Particle(x1, y1) {        this.x = x1 != null ? x1 : 0;        this.y = y1 != null ? y1 : 0;        this.reset();      }        Particle.prototype.reset = function() {        this.level = 1 + floor(random(4));        this.scale = random(SCALE.MIN, SCALE.MAX);        this.alpha = random(ALPHA.MIN, ALPHA.MAX);        this.speed = random(SPEED.MIN, SPEED.MAX);        this.color = random(COLORS);        this.size = random(SIZE.MIN, SIZE.MAX);        this.spin = random(SPIN.MAX, SPIN.MAX);        this.band = floor(random(NUM_BANDS));        if (random() < 0.5) {          this.spin = -this.spin;        }        this.smoothedScale = 0.0;        this.smoothedAlpha = 0.0;        this.decayScale = 0.0;        this.decayAlpha = 0.0;        this.rotation = random(TWO_PI);        return (this.energy = 0.0);      };        Particle.prototype.move = function() {        this.rotation += this.spin;        return (this.y -= this.speed * this.level);      };        Particle.prototype.draw = function(ctx) {        var alpha, power, scale;        power = exp(this.energy);        scale = this.scale * power;        alpha = this.alpha * this.energy * 1.5;        this.decayScale = max(this.decayScale, scale);        this.decayAlpha = max(this.decayAlpha, alpha);        this.smoothedScale += (this.decayScale - this.smoothedScale) * 0.3;        this.smoothedAlpha += (this.decayAlpha - this.smoothedAlpha) * 0.3;        this.decayScale *= 0.985;        this.decayAlpha *= 0.975;        ctx.save();        ctx.beginPath();        ctx.translate(this.x + cos(this.rotation * this.speed) * 250, this.y);        ctx.rotate(this.rotation);        ctx.scale(          this.smoothedScale * this.level,          this.smoothedScale * this.level        );        ctx.moveTo(this.size * 0.5, 0);        ctx.lineTo(this.size * -0.5, 0);        ctx.lineWidth = 1;        ctx.lineCap = 'round';        ctx.globalAlpha = this.smoothedAlpha / this.level;        ctx.strokeStyle = this.color;        ctx.stroke();        return ctx.restore();      };        return Particle;    })();      Sketch.create({      particles: [],      setup: function() {        var analyser, error, i, intro, j, particle, ref, warning, x, y;        for (i = j = 0, ref = NUM_PARTICLES - 1; j <= ref; i = j += 1) {          x = random(this.width);          y = random(this.height * 2);          particle = new Particle(x, y);          particle.energy = random(particle.band / 256);          this.particles.push(particle);        }        if (AudioAnalyser.enabled) {          try {            analyser = new AudioAnalyser(MP3_PATH, NUM_BANDS, SMOOTHING);            analyser.onUpdate = (function(_this) {              return function(bands) {                var k, len, ref1, results;                ref1 = _this.particles;                results = [];                for (k = 0, len = ref1.length; k < len; k++) {                  particle = ref1[k];                  results.push((particle.energy = bands[particle.band] / 256));                }                return results;              };            })(this);            analyser.audio = window.audio;            ANALYSER = analyser;            intro = document.getElementById('intro');            intro.style.display = 'none';            if (              /Safari/.test(navigator.userAgent) &&              !/Chrome/.test(navigator.userAgent)            ) {              warning = document.getElementById('warning2');              return (warning.style.display = 'block');            }          } catch (_error) {            error = _error;          }        } else {          warning = document.getElementById('warning1');          return (warning.style.display = 'block');        }      },      draw: function() {        var j, len, particle, ref, results;        this.globalCompositeOperation = 'lighter';        ref = this.particles;        results = [];        for (j = 0, len = ref.length; j < len; j++) {          particle = ref[j];          if (particle.y < -particle.size * particle.level * particle.scale * 2) {            particle.reset();            particle.x = random(this.width);            particle.y =              this.height + particle.size * particle.scale * particle.level;          }          particle.move();          results.push(particle.draw(this));        }        return results;      }    });  }    function handleFileSelect(evt) {    var files = evt.target.files;    getAnimation(files[0]);  }    getAnimation(null);    document    .getElementById('files')    .addEventListener('change', handleFileSelect, false);</pre>    </div>    <div>```</div>    <div>index.html</div>    <div></code></pre>    <pre>  <code class="language-javascript"><link rel="stylesheet" href="app.css">  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:400,800"></code></pre>    <h2>Play Music</h2>    <p><input type="file" id="files" name="files[]" multiple /></p>    <p>`<script crossorigin src="https://unpkg.com/react@15/dist/react.js">`</script> `<script crossorigin src="https://unpkg.com/react-dom@15/dist/react-dom.js">`</script> `<script src="https://npmcdn.com/babel-core@5.8.38/browser.min.js">`</script> `<script src="https://soulwire.github.io/sketch.js/js/sketch.min.js">`</script> `<script src="app.js">`</script> `<script type="text/babel">` // React code goes here. </script></p>    <p>```</p>    <p>确定你使用了最新版 <a href="/misc/goto?guid=4959755865710166781" rel="nofollow,noindex">chrome</a> ,不然代码中的动画效果无法呈现</p>    <p>谢谢 <a href="/misc/goto?guid=4959755865791392868" rel="nofollow,noindex">Steven Fabre</a> 提供的播放按钮CSS代码,还有 <a href="/misc/goto?guid=4959755865877799822" rel="nofollow,noindex">Justin Windle</a> 提供的可视化代码( <a href="/misc/goto?guid=4959755865961644544" rel="nofollow,noindex">你可以在这里查看代码</a> )</p>    <p>在代码编辑器里打开index.html,让我们开始吧!</p>    <h3>什么是React?</h3>    <p>React是构建用户界面的一种方式。它只和你在前端所见的相关。React通过把每个页面切分为小块让用户界面变得易于构建。我们把这些小块叫做组件。</p>    <p>这里有一个把页面切分为组件的例子:</p>    <p><img src="https://simg.open-open.com/show/c77a3e737ea0f2880a44d07fc08413a2.png" alt="关于React你应该了解的一切:开始构建前你所需的基础知识 " width="550" height="275"></p>    <p>每个高亮的部分都被视为一个组件。可这对于开发者意味着什么呢?</p>    <h3>什么是React组件?</h3>    <p>一个React组件就是一个代表页面某部分的代码块。每个组件都是一个JS函数,这个函数返回的代码表示一个web页面的一部分。</p>    <p>为了构建一个页面,我们将按一定顺序调用这些函数,然后把结果放在一起展示给用户。</p>    <p>让我们在index.html中写一个type="text/babel"的组件:</p>    <pre>  <code class="language-javascript"><script type="text/babel">`    function OurFirstComponent() {      return (        // Code that represents the UI element goes here      );    }  </script></code></pre>    <p>当我们调用OurFirstcomponent()函数,我们会得到页面的一个部分。</p>    <p>你可以像下面这样编写函数:</p>    <pre>  <code class="language-javascript">const OurFirstComponent = () => {    return (      // Stuff to make this component goes here    );  }</code></pre>    <p>React使用了JSX语言,它看起来像HTML但在JS里运行,HTML可不经常这么做。</p>    <p>你可以为这部分添加无样式的HTML,这样就可以在UI上显示了:</p>    <pre>  <code class="language-javascript"><script type="text/babel">`    function OurFirstComponent() {      return (        <h1>Hello, I am a React Component!</h1>      );    }  </script></code></pre>    <p>When we call the OurFirstComponent() function, we get back a bit of JSX. We can use something called <a href="/misc/goto?guid=4959755866047605627" rel="nofollow,noindex">ReactDOM</a> to put it on the page.</p>    <p>当我们调用OurFirstComponent函数,我们得到一些JSX代码。我们可以使用 <a href="/misc/goto?guid=4959755866047605627" rel="nofollow,noindex">ReactDOM</a> 把它们放在页面上。</p>    <pre>  <code class="language-javascript"><script type="text/babel">`    function OurFirstComponent() {      return (        <h1>Hello, I am a React Component!</h1>      );    }</code></pre>    <pre>  <code class="language-javascript">**const placeWeWantToPutComponent = document.getElementById('hook');    ReactDOM.render(OurFirstComponent(), placeWeWantToPutComponent);**  </script></code></pre>    <p>现在通过ID我们会将标签放入元素中。当你刷新浏览器时它看起来应该是这样:</p>    <p><img src="https://simg.open-open.com/show/289c3e269be05e55b9a180cb1f26146b.png" alt="关于React你应该了解的一切:开始构建前你所需的基础知识 " width="550" height="145"></p>    <p>我们可以像这样用JSX编写组件:</p>    <pre>  <code class="language-javascript">ReactDOM.render(<OurFirstComponent />, placeWeWantToPutComponent);</code></pre>    <p>这是标准-调用组件就像编写HTML,效果是一样的。</p>    <h3>把组件放在一起</h3>    <p>我们可以把React组件放在其他组件里。</p>    <pre>  <code class="language-javascript"><script type="text/babel">`    function OurFirstComponent() {      return (        **<h1>I am the child!</h1>**      );    }</code></pre>    <pre>  <code class="language-javascript">**function Container() {      return (        <div>          <h1>I am the parent!</h1>          <OurFirstComponent />        </div>      );    }**</code></pre>    <pre>  <code class="language-javascript">const placeWeWantToPutComponent = document.getElementById('hook');    ReactDOM.render(**<Container />**, placeWeWantToPutComponent);  </script></code></pre>    <p><img src="https://simg.open-open.com/show/2be8035d26b230a123bc799554bacba8.png" alt="关于React你应该了解的一切:开始构建前你所需的基础知识 " width="550" height="151"></p>    <p>我们就是这样通过React组件构造页面的-通过组件嵌套组件实现。</p>    <h3>类组件</h3>    <p>到目前为止,我们一直在像编写函数一样编写组件。它们被称为函数式组件.</p>    <p>但你也可以用另一种方式编写组件,比如JavaScript类。这样编写的组件被称作类组件。</p>    <pre>  <code class="language-javascript">class Container extends React.Component {    render() {      return (        <div>          <h1>I am the parent!</h1>          <OurFirstComponent />        </div>      );    }  }</code></pre>    <pre>  <code class="language-javascript">const placeWeWantToPutComponent = document.getElementById('hook');  ReactDOM.render(<Container />, placeWeWantToPutComponent);</code></pre>    <p>类组件一定要有一个render()函数.这个渲染函数返回组件的JSX代码。它们和函数化组件的使用方法相同。</p>    <p>你应该更多地使用函数化组件而不是类组件,因为前者更容易阅读,除非你需要组件状态(稍后介绍)</p>    <h3>JSX中的JavaScript</h3>    <p>你可以把JavaScript变量这样放进你的JSX中:</p>    <pre>  <code class="language-javascript">class Container extends React.Component {    render() {      **const greeting = 'I am a string!';**      return (        <div>          **<h1>{ greeting }</h1>**          <OurFirstComponent />        </div>      );    }  }</code></pre>    <p>现在I am a string会在h1标签内出现。</p>    <p>你也可以做点更难的,比如调用一个函数:</p>    <pre>  <code class="language-javascript">class Container extends React.Component {    render() {      **const addNumbers = (num1, num2) => {        return num1 + num2;      };**      return (        <div>          **<h1>The sum is: { addNumbers(1, 2) }</h1>**          <OurFirstComponent />        </div>      );    }  }</code></pre>    <p><img src="https://simg.open-open.com/show/c4cf8236dc8e566041252ae1758857d8.png" alt="关于React你应该了解的一切:开始构建前你所需的基础知识 " width="550" height="138"></p>    <h3>JSX可伸缩性</h3>    <p>将OurFirstComponent()更名为PlayButton。我们想要它返回下面的内容:</p>    <pre>  <code class="language-javascript"><a href="#" title="Play video" /></code></pre>    <p>可现在有个问题:class在JavaScript中是关键字,所以我们无法使用它。那我们该如何创建一个示例?</p>    <p>用一个名字叫className的属性代替:</p>    <pre>  <code class="language-javascript"><script type="text/babel">`   **function PlayButton() {      return <a href="#" title="Play video" />;    }**</code></pre>    <pre>  <code class="language-javascript">class Container extends React.Component {      render() {        return (          <div>            **<PlayButton />**          </div>        );      }    }</code></pre>    <pre>  <code class="language-javascript">const placeWeWantToPutComponent = document.getElementById('hook');    ReactDOM.render(<Container />, placeWeWantToPutComponent);  </script></code></pre>    <h3>这个组件在干什么?</h3>    <p>类组件可以存储关于它们最近状态的信息。这个信息被称为状态,储存在一个JavaScript对象中。</p>    <p>下面的代码里,有一个对象代表我们组件的状态。对象有一个键名叫isMusicPlaying,值为false。这个对象被以建造者模式分配给this.state,当这个类首次使用时this.state被调用。</p>    <pre>  <code class="language-javascript">class Container extends React.Component {    **constructor(props) {      super(props);      this.state = { isMusicPlaying: false };    }**      render() {      return (        <div>          <PlayButton />        </div>      );    }  }</code></pre>    <p>一个React组件的建造者模式总是需要在最开始就调用super(props)。</p>    <p>好,那我们要状态干什么呢?为什么它会存在?</p>    <h3>基于状态改变我们的React组件</h3>    <p>可以基于事件的状态来更新我们的UI。</p>    <p>在本文中,我们会基于用户点击播放按钮的操作,利用状态实现播放按钮从停止到播放的改变。</p>    <p>当用户点击按钮,状态就会更新,然后UI就会更新。</p>    <p>我们按下面的步骤开始。我们可以利用this,state监视组件状态。下面的代码中,我们持续监视状态并根据它来决定展示给用户什么样的内容。</p>    <pre>  <code class="language-javascript">class Container extends React.Component {    constructor(props) {      super(props);      this.state = { isMusicPlaying: false };    }</code></pre>    <pre>  <code class="language-javascript">render() {      **const status = this.state.isMusicPlaying ? 'Playing' : 'Not playing';**      return (        <div>          **<h1>{ status }</h1>**          <PlayButton />        </div>      );    }  }</code></pre>    <p>在这个渲染函数中,this经常表示它所在的组件。</p>    <p><img src="https://simg.open-open.com/show/633332b18dfb5c5786e2fd920db3f23d.png" alt="关于React你应该了解的一切:开始构建前你所需的基础知识 " width="550" height="170"></p>    <p>但这并没有什么用处,除非我们有方法可以改变this.state.isMusicPlaying。</p>    <h3>当我们的组件发生变化的时候</h3>    <p>用户可以通过点击播放按钮与我们的组件实现交互。我们想对这些事件做出反应(ha...ha...)。</p>    <p>我们通过与事件相关的函数实现交互操作。我们称之为事件句柄。</p>    <pre>  <code class="language-javascript">class Container extends React.Component {    constructor(props) {      super(props);      this.state = { isMusicPlaying: false };    }</code></pre>    <pre>  <code class="language-javascript">**handleClick(event) {      // Do something about the click    };**</code></pre>    <pre>  <code class="language-javascript">render() {      let status = this.state.isMusicPlaying       ? 'Playing :)'       : 'Not playing :(';      return (        <div>          <h1>{ status }</h1>          <PlayButton />        </div>      );    }  }</code></pre>    <p>当用户点击h1时,我们的组件会运行handleClick函数。这个函数将得到的时间对象作为参数,这意味着它可以随心所欲地使用该对象。</p>    <p>我们在handleClick上使用.bind方法,以确保this特指整个组件,而不只是h1。</p>    <h3>那这个组件应该干什么</h3>    <p>当我们改变了组件的状态,它会再次调用渲染函数。</p>    <p>我们可以随this.setState()改变状态,只要我们给它一个表示新状态的新对象。</p>    <p>我们页面中的组件会始终展示它们的最新状态。这是React替我们做的。</p>    <pre>  <code class="language-javascript">handleClick() {      **if (this.state.isMusicPlaying) {        this.setState({ isMusicPlaying: false });      } else {        this.setState({ isMusicPlaying: true });      }**    };</code></pre>    <p>可是点击一个h1还是不如点击播放按钮方便。让我们动手实现一下吧。</p>    <h3>组件间的通信</h3>    <p>你的组件可以互相通信。让我们试一下。</p>    <p>我们可以用属性告知播放按钮音乐是否在播放。属性是一个父组件分享给子组件的信息。</p>    <p>JSX中的属性看起来和HTML属性差不多。</p>    <p>我们给播放按钮设置一个名为isMusicPlaying的属性,它和this.state里的isMusicPlaying一样。</p>    <pre>  <code class="language-javascript">class Container extends React.Component {    constructor(props) {      super(props);      this.state = { isMusicPlaying: false };    }</code></pre>    <pre>  <code class="language-javascript">handleClick() {      if (this.state.isMusicPlaying) {        this.setState({ isMusicPlaying: false });      } else {        this.setState({ isMusicPlaying: true });      }    };</code></pre>    <pre>  <code class="language-javascript">render() {      return (        <div>   **<PlayButton isMusicPlaying={this.state.isMusicPlaying} />**      </div>      );    }  }</code></pre>    <p>当Container的状态改变,PlayButton属性也会改变,PlayButton函数也会被再次调用。这意味着我们的组件会在屏幕上更新。</p>    <p>在PlayButton里,我们可以对改变做出反应,因为PlayButton会将得到的属性作为参数。</p>    <pre>  <code class="language-javascript">function PlayButton(**props**) {   **const className = props.isMusicPlaying ? 'play active' : 'play';**  return <a href="#" title="Play video" />;  }</code></pre>    <p>如果我们改变状态为this.state = {isMusicPlaying:true };,然后刷新页面,你应该能看到暂停按钮。</p>    <p><img src="https://simg.open-open.com/show/8eda7ef8f34cff7bf5442b130fcc3aa2.png" alt="关于React你应该了解的一切:开始构建前你所需的基础知识 " width="550" height="144"></p>    <h3>基于属性的事件</h3>    <p>你的属性没有必要必须为信息。它们可以是函数。</p>    <pre>  <code class="language-javascript">function PlayButton(props) {    const className = props.isMusicPlaying ? 'play active' : 'play';    return <a href="#" title="Play video" />;  }</code></pre>    <pre>  <code class="language-javascript">class Container extends React.Component {    constructor(props) {      super(props);      this.state = { isMusicPlaying: false };    }</code></pre>    <pre>  <code class="language-javascript">handleClick() {      if (this.state.isMusicPlaying) {        this.setState({ isMusicPlaying: false });      } else {        this.setState({ isMusicPlaying: true });      }    };</code></pre>    <pre>  <code class="language-javascript">render() {      return (        <div>          <PlayButton             **onClick={this.handleClick.bind(this)}**             isMusicPlaying={this.state.isMusicPlaying}           />        </div>      );    }  }</code></pre>    <p>现在,当我们点击播放按钮,Container的状态会改变,这会改变播放按钮的属性,导致按钮在页面上刷新。</p>    <h3>设置状态的坏处</h3>    <p>setState很糟糕,因为它不会立即响应。React会等待一会以便查看是否有更多改变要做,之后才会改变状态。</p>    <p>这意味着你不确定当你调用setState时你的状态会变成什么。</p>    <p>所以你不应该这样做:</p>    <pre>  <code class="language-javascript">handleClick() {    this.setState({ isMusicPlaying: !this.state.isMusicPlaying });  };</code></pre>    <p>如果你是基于旧状态来改变新状态,你需要做点不一样的工作。</p>    <p>你需要赋给getState一个函数而不是对象。这个函数获得旧状态作为一个参数,返回一个新对象,这个对象即为新状态。</p>    <p>它看起来像是这样:</p>    <pre>  <code class="language-javascript">handleClick() {    this.setState(prevState => {      return {         isMusicPlaying: !prevState.isMusicPlaying         };    });  };</code></pre>    <p>这更困难,但只有当你使用旧状态来获得新状态时才需要。如果不是,你可以只赋给setState一个对象。</p>    <h3>什么是Refs?</h3>    <p>让我们试试播放音乐。</p>    <p>First, we add an `` tag: 首先,我们添加一个标签:</p>    <pre>  <code class="language-javascript">class Container extends React.Component {    constructor(props) {      super(props);      this.state = { isMusicPlaying: false };    }</code></pre>    <pre>  <code class="language-javascript">handleClick() {      this.setState(prevState => {        return {           isMusicPlaying: !prevState.isMusicPlaying           };      });    };</code></pre>    <pre>  <code class="language-javascript">render() {      return (        <div>          <PlayButton             onClick={this.handleClick.bind(this)}             isMusicPlaying={this.state.isMusicPlaying}           />   **<audio />**      </div>      );    }  }</code></pre>    <p>我们需要一种方法来获得标签,然后在该标签上调用play()或者pause()。我们可以用document.getElementById('audio').play()实现,但还有更好的React方法。</p>    <p>我们赋给它一个属性ref,该属性和被作为第一个参数的元素一起被调用。它获得这个元素并把元素分配给this.audio。</p>    <pre>  <code class="language-javascript"><audio> { this.audio = audioTag }}** /></code></pre>    <p>Container每次进行渲染时都会调用这个函数,这意味着this.audio会始终保持更新。</p>    <p>我们这时可以播放和暂停音乐:</p>    <pre>  <code class="language-javascript">handleClick() {    **if (this.state.isMusicPlaying) {      this.audio.pause();    } else {      this.audio.play();    }**    this.setState(prevState => {      return {         isMusicPlaying: !prevState.isMusicPlaying         };    });  };</code></pre>    <p>使用选择文件按钮上传一个音乐文件(mp3文件更合适),然后点击播放就能欣赏音乐啦。</p>    <h3>跳出index.html</h3>    <p>和你想的一样,我们的React不应该围着标签转。</p>    <p>React需要很多构建配置。幸运的是,一些工具比如 <a href="/misc/goto?guid=4958996917562467082" rel="nofollow,noindex">Create React App</a> 为你做好了一切。</p>    <p>安装该工具来创建你自己的React项目。跟着它们简洁的文档,开始编辑src目录里的JavaScript代码,把你所学到的React知识应用在这吧。</p>    <h3>祝贺</h3>    <p>现在你可以用React做一些东西了。</p>    <p>接下来,阅读其他文章来获得更多信息。一篇是 <a href="/misc/goto?guid=4959755866180187279" rel="nofollow,noindex">React best practices</a> ,另一篇是关于React一个有用的部分 <a href="/misc/goto?guid=4959755866261467498" rel="nofollow,noindex">lifecycle methods</a> .</p>    <p>如果你从本篇文章中学到了一点东西,请为我们点赞,并分享给你的朋友们。</p>    <p>来自:</p>    <p> </p>