我在一周内学会了如何在 React 中提高效率,你也可以

Avatar of Sarah Drasner
Sarah Drasner

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费积分!

这篇文章不是针对经验丰富的 React 专业人士,而是针对我们这些以网站为生且好奇 React 如何帮助我们理解用户界面更新的人。我一直对 React 很感兴趣,现在它在社区中获得了认可并获得了不错的评价,学习它的时机似乎已经成熟。前端开发中不断涌现出如此多的新技术,以至于有时很难知道学习新技术的努力是否会得到回报。在本文中,我将介绍我认为一些最有价值的实用见解,以便您能够开始使用它。

公平警告

我只花了大约一周时间学习这些材料。这是故意的。我是在接触这项技术不久后就写下这篇文章的,并且可以从那个角度来写它。在这种状态下,我更擅长记住一路走来的跌跌撞撞和发现。

我使用了 Wes Bos 的 React 入门 课程,以及 React.js 入门,适用于那些仅懂少量 jQuery 的人。我强烈推荐这些资源。

两天内,我做出了这个

查看代码笔 宝宝第一次尝试 React,作者 Sarah Drasner (@sdras) 在 CodePen 上。

它并不完美,但它比我在那段时间内独自做出的东西要好得多。

如果您是那种想直接深入学习所有内容的人,包括构建和必需的工具,那就太棒了!从这里开始吧。 我还会推荐这个非常棒的 Frontend Masters 课程:现代 Web 应用程序

流言终结者:React 实用性版本

在开始之前,我想破除一些我认为是许多人阻碍的几个关键神话。

神话 #1:您必须使用内联样式才能使用 React。

不!根本不是。您可以像往常一样使用 CSS。我刚花了大量时间重构一个巨大的 CSS 代码库,我会说建立这一点非常重要。React 比较新,还没有像其他技术一样经受住设计刷新的考验。如果我不得不重新遍历数千行内联样式才能更新像 paddingline-height 这样的东西,那确实会让我成为一个悲伤的开发者。

也就是说,内联样式在某些情况下确实很有用。如果您有一个组件,其样式会根据其状态而改变(例如:数据可视化),那么使用内联样式绝对有意义,这样您就不必维护不切实际数量的静态样式(在多个地方)来处理所有可能的状态。

不过我认为,这将建立在应用程序使用的基础 CSS 之上,并且是例外而不是规则。不过,网络很大,所以没有绝对的规定。

神话 #2:您必须使用 JavaScript 语法来表示元素属性,这与 HTML 完全不同。

我喜欢 Wes Bos 的教学风格的一点是,他会引导观众转向更熟悉的方法和实现。我通常喜欢简单和代码更清晰的方式,尽管我理解其他人喜欢更高程度的抽象。

他建议我们使用 JSX 来编写我们的标记,它更像我们的朋友传统 HTML,因此我发现它更清晰。所以,不用这样写

return React.createElement("p", {className: "foo"}, "Hello, world!");

我们将这样写

return (<p className="foo">Hello, world!</p>);

两种方法都可以。但是,当我们的标记变得越来越复杂时,我发现 JSX 中的 HTML 熟悉性有助于我的理解。不过请记住,即使使用 JSX,也有一些细微的差别。

神话 #3:为了尝试 React,您必须理解所有构建工具。

确实,为了使用 React,您需要使用构建工具,所以这通常是教程的开头。但是,我建议,作为一个完全的初学者,您应该在 CodePen 上玩玩。这是一个快速迭代和学习的好方法,在将大量时间投入一项全新的技术之前。

在典型应用程序中使用 React

在使用 React 1.14+ 的典型应用程序中,我们将从需要 React 和 ReactDOM 开始

var React = require('react');
var ReactDOM = require('react-dom');

然后调用 ReactDOM.render

ReactDOM.render(routes, document.querySelector('#main'));

在 CodePen 中使用 React

在我们的例子中,我们只需从 JS 面板的下拉菜单中选择 React(单击面板顶部的齿轮图标),然后使用 Babel 作为编译器。

我们不需要 require React 或 React DOM,因为我们没有路由任何内容,我们直接使用应用程序组件。我们的代码将简单地修改为

React.render(<App/>, document.querySelector("#main"));
<div id="main"></div>

此外,为了开始使用,您需要 Chrome 的 React Devtools 扩展,或 Firefox 的 React Devtools 扩展,它非常适合调试我们的虚拟 DOM。

在 CodePen 中,您可以使用 调试视图,它将正确地找到 React

运行起来

您需要了解的一些基本构建块如下

// App
var App = React.createClass({
  render: function() {
    return (
      <div className="foo">Hello, world!</div>
    )
  }
});
 
ReactDOM.render(<App/>, document.querySelector("#main"));

查看代码笔 Hello World React,作者 Sarah Drasner (@sdras) 在 CodePen 上。

让我们分解一下。在最后一行,我们找到了 main div id,并且在上面我们渲染了 <App /> 组件,它将加载我们所有的 React 应用程序。您现在可以使用 Devtools 中的 React 选项卡查看我们创建的 DOM 元素。

我们已将 <App /> 作为第一个组件附加。这里要注意的第一点是,此标签是大写的 - 虽然这不是必需的,但它是 React 组件的最佳实践。第二点是,它是自闭合的。React 中的标签必须关闭,方法是使用额外的闭合标签(例如 </div>),或自闭合标签(例如 <hr> 将变为 <hr />)。这只是 JSX 的工作方式,否则会抛出错误。

请注意顶部创建应用程序组件的结构。您将非常习惯这种语法,因为我们今天构建的所有内容都将基于这个关键构建块。

最后一点需要注意的是,我们没有使用 `class`,而是使用 `className` 属性在元素上。如果你习惯于编写网页的 HTML 标记,这可能是一个需要注意的地方。幸运的是,它很容易解决。有关 JSX 及其语法的更多信息,这些文档 非常好的资源。

// App
var App = React.createClass({
  render: function() {
    return (
      <Header />
    )
  }
});
 
// Header
var Header = React.createClass({
 render: function() {
   return (
     <div className="foo">Hello, world!</div>
   )
 }
});
 
ReactDOM.render(<App/>, document.querySelector("#main"));

查看 CodePen 上 Sarah Drasner 创建的 Hello World React (@sdras) 。

在下一步中,我们将用一个组件扩展 app。Header 可以命名为任何你想要的名称,为了清楚起见,我们将其命名为它在文档中的角色。你可以看到,如果我们还想添加一个导航元素,那就很容易添加了。以下是同一个页面,只是使用了一个标准的 Bootstrap 行按钮作为 `<Nav />` 组件,以及一个更语义化的 `h1` 来显示我们的“Hello, World!”,而不是 `div`。

// App
var App = React.createClass({
render: function() {
 return (
   <div>
     <Nav />
     <Header />
   </div>
  )
 }
});
 
// Nav
var Nav = React.createClass({
 render: function() {
   return (
    <ul className="nav nav-pills">
      <li role="presentation" className="active">Home</li>
      <li role="presentation">Profile</li>
      <li role="presentation">Messages</li>
    </ul>
  )
 }
});
 
// Header
var Header = React.createClass({
 render: function() {
   return (
     <h1 className="foo">Hello, world!</h1>
   )
 }
});
 
ReactDOM.render(<App/>, document.querySelector("#main"));

查看 CodePen 上 Sarah Drasner 创建的 Hello World React (@sdras) 。

非常简单,对吧?它就像任何网页的积木一样。我们创建了 nav 和 header,并将这些组件应用于 app 组件,该组件在 body 上渲染。最后一个示例中唯一的真正需要注意的地方是你在 `<Header />` 和 `<Nav />` 周围看到的奇怪的额外 div。这是因为我们必须始终返回单个元素。我们不能有两个兄弟元素,所以我们将它们包装在一个 div 中。

既然你了解了构建块,你就可以轻松地使用 React 和 React 组件拼凑出一个页面。我建议你重构一个你已经制作的页面布局,这样你就可以学习了。将样式表放在里面,然后一点一点地拼凑出剩下的部分,直到你重构了它的骨架。这是一个很好的练习。

有一种方法可以 自动化此过程,但我建议在你先自己完成构建的过程之前不要使用这样的工具。这样,如果你以后遇到问题,你就知道发生了什么。

常见嫌疑人:变量和事件处理

让我们先进行一些简单的事件处理并添加一些简单的变量,看看效果如何。

再次考虑第一个演示(我们将在整篇文章中使用它)

查看代码笔 宝宝第一次尝试 React,作者 Sarah Drasner (@sdras) 在 CodePen 上。

博客文章组件有一些非常简单的标记,所以让我们从它开始

// Blog Post
var Post = React.createClass({
  render : function() {
    return (
      <div className="blog-post">
        <h3 className="ptitle">{this.props.ptitle}<small>{this.props.date}</small></h3>
        <img className="thumbnail" src={this.props.pimg} />
        <p>{this.props.postbody}</p>
        <div className="callout callout-post">
          <ul className="menu simple">
          <li>Author: {this.props.author}</li>
          <li>Comments: {this.props.comments}</li>
          <li>Tags: {h.getTaggedName()}</li>
          </ul>
        </div>
      </div>
    )
  }
});

让我们添加一个任意变量和事件处理程序,看看它是如何工作的。首先要考虑的是,如果我们使用 JSX,花括号将让 React 知道我们准备使用一些 JavaScript。因此,我们的变量将类似于 ` {var} `。

我们将在 render 函数调用内部添加变量语句,但在返回标记之前。然后,我们可以通过调用变量来访问它,在本例中为 ` {com} `。单击事件是一个内联 ` onClick ` 处理程序,这可能与你习惯的最佳实践背道而驰。这是一个我们选择的名称,而 render 是一个框架方法。我们调用 ` {this.tryClick} `,并在同一个组件中编写一个名为 ` tryClick `(一个任意名称)的方法,如下所示

// Blog Post
var Post = React.createClass({
  tryClick : function() {
    alert('just trying out click events lalala');
  },
  render : function() {
    var com = "Comments";
    return (
      <div className="blog-post">
        <h3 className="ptitle">{this.props.ptitle}<small>{this.props.date}</small></h3>
        <img className="thumbnail" src={this.props.pimg} />
        <p>{this.props.postbody}</p>
        <div className="callout callout-post">
          <ul className="menu simple">
          <li>Author: {this.props.author}</li>
          <li>{com}: {this.props.comments}</li>
          <li>Tags: {h.getTaggedName()}</li>
          </ul>
        </div>
      </div>
    )
  }
});

React 中事件的语法以“on”开头,并且使用驼峰命名法。例如

  • click = onClick
  • mouseEnter = onMouseEnter
  • keyPress = onKeyPress

你可以找到所有受支持事件的完整列表

将 React 与其他库结合使用(在本例中为 Greensock)

我喜欢使用 GSAP 进行动画制作。你可以在 React 中使用其他库,所以让我们将 GSAP 和 React 结合起来,看看效果如何。

我们希望在正确的时间访问其他库,因此我们必须确保它们在第一个 render 方法之后立即被调用。我们为此使用的方法称为 ` componentDidMount `。我们将在后面更多地解释其他生命周期方法,但你现在需要知道的是,该方法是在组件插入文档后立即调用的。

如果你习惯使用 jQuery,你熟悉从 DOM 中获取元素。在本例中,即使我们正在对 DOM 元素进行补间动画,我们也将使用 React 的 ` getDOMNode()` 而不是直接获取它们。你还会看到,我们实际上是在 app 组件中调用进行补间动画的函数,并且只是将其传递给我们的盒子。(你可能需要点击重新运行才能看到动画。)

查看 CodePen 上 Sarah Drasner 创建的 794b375a8725b039483c83fb63a4bd5a (@sdras) 。

// App
var App = React.createClass({
 componentDidMount: function() {
    var sq1 = this.refs.first.getDOMNode();
    var sq2 = this.refs.second.getDOMNode();
    var sq3 = this.refs.third.getDOMNode();
    var sq4 = this.refs.fourth.getDOMNode();
    var sq5 = this.refs.fifth.getDOMNode();
    var allSq = [sq1, sq2, sq3, sq4, sq5];
   
    TweenLite.set(allSq, {css:{transformPerspective:400, perspective:400, transformStyle:"preserve-3d"}}); 

    TweenMax.to(sq1, 2.5, {css:{rotationX:230, z:-600}, ease:Power2.easeOut}, "+=0.2");
    TweenMax.to(sq2, 2.5, {css:{rotationY:230, z:-150}, ease:Power4.easeOut}, "+=0.2");
    TweenMax.to(sq3, 2.5, {css:{rotationX:500, z:150}, ease:Power2.easeInOut}, "+=0.2");
    TweenMax.to(sq4, 2.5, {css:{rotationY:500, z:-150}, ease:Power4.easeOut}, "+=0.2");
    TweenMax.to(sq5, 2.5, {css:{rotationX:1000, z:100}, ease:Power2.easeOut}, "+=0.2");
 },
 render: function() {
   return (
     <div className="scene">
        <Box ref="first"></Box>
        <Box ref="second"></Box>
        <Box ref="third"></Box>
        <Box ref="fourth"></Box>
        <Box ref="fifth"></Box>
    </div>
   )
 }
});

// Box
var Box = React.createClass({
  render: function() {
    return (
      <div className="squares"></div>
    )
  }
});
 
ReactDOM.render(<App/>, document.querySelector("#main"));

你可能习惯于通过 jQuery 或原生 JavaScript 访问 DOM。我们仍然可以这样做,但在这里,我们通过 ` getDOMNode()` 和 ` refs` 访问 DOM 的部分,例如这行代码:` var sq1 = this.refs.squares1.getDOMNode(); `。你可以在 React 文档 中阅读更多关于此内容的信息。

有更多特定于 React 的方法可以将动画添加到你的项目中——一个非常棒的方法是 Cheng Lou 的 React-Motion,另一个值得一提的是 React-GSAP-Enhancer

创建全局辅助函数

你甚至可以编写辅助函数,这些函数可以轻松地被多个组件访问。我们将使用与 ` this.functionName ` 相同的点表示法来使用它们。我们存储辅助函数(在本例中,我们在 CodePen 演示的开头存储,但在实际应用程序中,它们将存储在单独的文件中),并像你在组件结构中一样将每个函数声明为一个对象。

类似于第一个演示中博客文章中格式正确的日期,将是

var h =  {
  getTime: function() {
     var month = ["jan", "feb", "march"]; // …. and so on
     var d = new Date();
     var mon = month[d.getMonth()];
     var day = d.getDate();
     var year = d.getFullYear();
     var dateAll = mon + " " + day + ", " + year;
   
     return dateAll;
  }
};

用法如下:` {h.getTime()} `

好东西:管理状态

好了!现在我们已经掌握了构建块,让我们来学习一些很酷的东西。

React 当然擅长使用易于理解的小巧组件构建应用程序,但它真正擅长的是管理这些组件的状态。让我们深入了解一下。

对于这一部分,我们将介绍了解如何访问和使用变化的事物时需要理解的两个关键部分。

  1. 第一个是**状态**。组件本身拥有它,这意味着它对组件进行了范围限定,我们将这样引用它:` {this.state.foo} `。我们可以通过调用 ` this.setState()` 来更新状态。
  2. 第二个是指我们如何从父组件传递只读数据到子组件(例如,app 是第一个示例中 ` header ` 的父组件)。我们将它称为**属性**,就像属性一样,我们将直接在组件中使用它,使用 ` {this.props.foo} `。子组件无法更改此数据。

只要我们更改了这两者中的任何一个,并且我们的组件依赖于它,React 就会重新渲染它需要更新的组件部分。

虚拟 DOM 的妙处在于,React 会找出哪些 DOM 节点需要更新。

我已经阅读了很多关于状态的内容,但我认为 Wes 的解释最清晰,所以我将用他的话来解释:**如果你习惯使用 jQuery,你将在 DOM 中存储所有数据。React 通过将数据存储在对象(状态)中,然后根据对象渲染事物,完全避免了这种情况。这就像一个巨大的主控台,所有东西都基于它。**

尝试属性

让我们先尝试 ` this.props `,我们将 ` {this.props.title} ` 添加到 ` Header ` 组件,然后我们可以在 app 中访问它。我们将添加一个字符串来创建标题

// App
var App = React.createClass({
  render: function() {
    return (
      <Header greeting="Hello, world!" />
    )
  }
});
 
// Header
var Header = React.createClass({
  render: function() {
    return (
      <div className="foo">
        <h1>{this.props.greeting}</h1>
      </div>
    )
  }
});
 
ReactDOM.render(<App/>, document.querySelector("#main"));

我们也可以在名为 getDefaultProps 的生命周期方法中为道具添加默认值。这样做很棒,因为有时您不希望必须为每个组件指定道具。一个例子是,如果用户尚未设置默认的个人资料头像,例如 Twitter 上的鸡蛋,或者将列表设置为一个空数组,该数组将在稍后填充。

var ProfilePic = React.createClass({
  getDefaultProps: function() {
    return {
      value: 'twitter-egg.jpg'
    };
   ...
});

做点事情

现在让我们以此为基础,与状态进行一些交互。如果我们最终要更改 this.state,我们必须准备好它。为此,我们将使用 getInitialState,它允许我们定义状态的初始值。如果没有它,我们就无法添加状态。

我们只需要根据用户的选择更改背景。

查看 CodePen 上 Sarah Drasner 的作品 928579628fd2ecb0f98aff9c08774e93 (@sdras) on CodePen

// App
var App = React.createClass({
  /*setting state*/
  getInitialState: function() {
    return {
      bgColor: "teal"
    };
  },
  /*changes state*/
  handleColorChange: function (color) {
    // when we set state directly, react doesn't know
    // about it. that's why we use setState
    this.setState({ bgColor: color });
  },
  /*for the lifecycle methods*/
  updateBackgroundColor: function () {
    var body = document.querySelector('body')
    body.style.background = this.state.bgColor
  },
  /*lifecycle methods*/
  componentDidMount: function () { 
    this.updateBackgroundColor()
  },
  componentDidUpdate: function () { 
    this.updateBackgroundColor()
  },
  render: function() {
    return (
    /* by calling the title here on the component, we can access this.props on header */
      <div className="foo">
        <h1>Hello, World!</h1>
        <label>What color?
          <ColorPicker value={this.state.bgColor} onColorChange={this.handleColorChange}/>
        </label>
      </div>
    )
  }
});

// ColorPicker component
var ColorPicker = React.createClass({
  propTypes: {
    value: React.PropTypes.string.isRequired,
    onColorChange: React.PropTypes.func
  },
  handleChange: function(e) {
    e.preventDefault();
    var color = e.target.value
    
    // If whoever rendered us (the ColorPicker) is interested
    // when the color changes, let them know
    if (this.props.onColorChange)
      this.props.onColorChange(color);
  },
  render: function() {
    return (
      <select value={this.props.value} onChange={this.handleChange}>
        <option value="orangered">orangered</option>
        <option value="teal">teal</option>
        <option value="orange">orange</option>
        <option value="indigo">indigo</option>
        <option value="red">red</option>
      </select>
    )
  }
});

ReactDOM.render(<App/>, document.querySelector('#main'));

需要考虑的一个重要部分是,我们需要 handleChange 函数。

在普通的 DOM 环境中,原生选择器不需要您执行类似的操作,因为它会自动更新到用户的选择。在 React 中,所有状态都由组件管理,因此如果您需要一个输入或选择器来监听更改,您必须编写它。这意味着组件负责状态,而不是用户。

这对于少量回报来说似乎需要做很多工作,但我们特意将这些示例保持得相当简单。React 的强大之处在于处理复杂性。尽管我喜欢 jQuery(我仍然喜欢 jQuery,它不是布尔值),但当某些内容发生更改时,您必须经常执行检查。React 通过消除这种需求来解决这个问题,因为数据流方向的简单性,这也使得在高度复杂的应用程序中更容易推理。

状态概念

既然我们已经掌握了最基本的内容,那么让我们快速了解一下我们为什么要这样做,以及一些最佳实践。

当我们更新状态并重新渲染低级别组件时,React 才能发挥最佳效果。从根本上说,我们希望这些组件基本上是无状态的,并从上层组件接收数据。我们层次结构顶部的组件,例如我们的应用程序,在它们主要是状态化时效果最佳。这样,它们就可以管理大多数交互逻辑,并使用道具传递状态。也就是说,仍然会有一些情况需要每个组件都有自己的状态。但是,重要的是要记住,兄弟节点不应该直接相互通信,它们应该与它们的父组件通信。

因此,我们希望尽可能将状态保存在组件中。您实际上永远不必担心何时渲染内容。您只需要担心状态何时发生变化,React 会为您处理渲染。

这种最佳实践意识形态直接导致了我建议您不要在 getInitialState 中使用道具的原因,这里有更多关于为什么这被认为是一种反模式的信息。

我们不想过度使用状态。组件的渲染速度很快,但是如果您开始尝试管理过多的状态,性能可能会下降。有一些方法可以解决这个问题,一旦它真正成为问题,但是最好一开始就不要陷入困境。

本文的其余部分将介绍如何最好地管理这些状态,并将不可变的关注点保持在层次结构中的更高层级,同时仍然引用我们层次结构中较低层的无状态组件。

引用

我们还可以使用称为引用的东西访问有关元素的信息。我们通过将其附加到任何组件来使用引用。它将在 render() 方法中返回,然后我们可以在 render() 方法之外引用它。这非常有用。

再考虑一下第一个 CodePen。让我们花一分钟时间思考一下我们是如何创建这些新博客文章的,并在此过程中使用引用。

在每个表单输入中,我们都有一个引用,例如

<input type="text" ref="name" placeholder="Full Name required" required />

在表单元素本身,我们调用

onSubmit={this.createPost}

它引用了渲染函数上面的 createPost 函数,并使用引用来存储来自该提交的信息

var post = {
 name : this.refs.name.value,
 ...
}

然后我们有办法使用 this.props 在应用程序状态中引用它

this.props.addPost(post);
refs-overview

这非常方便,因为现在我们有一个名为 post 的变量,其中包含存储其名称(此处显示)、日期、详细信息等的 对象。我们还没有将这些内容存储在应用程序状态中,我们只是创建了一种方法让应用程序使用它。让我们接下来讨论一下,以及 keys

我使用这个示例作为一般讨论引用的方法,但是在现实应用程序中,您可能想考虑使用类似于form-serialize 的东西来序列化表单字段以通过 AJAX 提交。

键和使用引用

在我们上一节中,我们创建了一种方法来存储我们在表单中收集的数据,而不会将其传递出去。这是因为在 React 中,我们希望在层次结构的顶层管理这个状态。为了存储这个状态,我们使用一个空对象启动我们的初始状态。最终,我们将在其中存储我们的帖子数据。

getInitialState : function() {
  return {
    posts: {}
  }
},

然后我们有我们的 addPost 函数。请注意,我们在这里管理它,而不是在表单上,即使我们是从表单中调用的它。我们为状态提供一个时间戳以保持帖子的唯一性,并且还设置了帖子的状态。

addPost: function(post) {
  var timestamp = (new Date()).getTime();
  // update the state object
  this.state.posts['post-' + timestamp] = post;
  // set the state
  this.setState({ posts : this.state.posts });
},

数组中的每个子元素都应该具有唯一的 key.prop。这很重要,因为 React 会尽可能地重用现有的 DOM。React 使用 keys 来跟踪它应该更新 DOM 中的哪些元素。它使我们不必在重新渲染时重新绘制所有 DOM 元素。这有助于提高我们应用程序的性能。

现在让我们再看一下 App,看看我们如何处理从表单中新存储的数据

// App
var App = React.createClass({
  getInitialState : function() {
    return {
      posts : {}
    }
  }, 
  addPost : function(post) {
    var timestamp = (new Date()).getTime();
    // update the state object
    this.state.posts['post-' + timestamp] = post;
    // set the state
    this.setState({ posts : this.state.posts });
  },
  renderPost : function(key){
    return <NewPost key={key} index={key} details={this.state.posts[key]} />
  },
  render : function() {
   ...
    return (
      <div>
        <Banner />
	    ...
          <div className="list-of-posts">
              {Object.keys(this.state.posts).map(this.renderPost)}
          </div>
          <Submissions addPost={this.addPost}/>
        </div>
      </div>
    )
  }
});

您可以看到,当我们渲染应用程序时,我们使用 Object.keys 创建一个新数组。然后我们使用 .map() 以及我们之前创建的 renderPost 函数。对于那些不熟悉 .map() 的人来说,它对于从现有数组中创建数组非常有用,您可以将其视为循环。

<div className="list-of-posts">
  {Object.keys(this.state.posts).map(this.renderPost)}
</div>

现在让我们创建我们刚刚调用的那个 renderPost 函数。我们将其作为参数传递键,并将它设置为键、索引,并创建一个名为 details 的对象来保存关于帖子状态的信息。

renderPost: function(key) {
  return <NewPost key={key} index={key} details={this.state.posts[key]} />
},

我们传递一个 key 和一个 index 似乎很奇怪,但是在组件内部,我们无法访问 key prop,因此我们传递另一个名为 index 的属性。此 renderPost 函数只是为数组中的每个项目创建一个 <NewPost /> 组件,并传递下游 details,我们可以访问它。这很棒,因为它意味着如果我们的状态中有什么东西更新了,它就会传播到下游,并且我们可以在一个地方管理它。

这是 <NewPost /> 组件,我们使用从应用程序传递下来的 details 来渲染所有信息。您可以看到我们之前为正确格式化日期而创建的辅助函数,在本文中用作 {h.getTime()}

/*
  NewPost
  <NewPost />
*/
var NewPost = React.createClass({
  render : function() {
    var details = this.props.details;
    return (
      <div className="blog-post">
        <h3 className="ptitle">{details.title}<small>{h.getTime()}</small></h3>
        <img className="thumbnail" src={details.image} alt={details.name}/>
        <p>{details.desc}</p>
        <div className="callout callout-post">
          <ul className="menu simple">
          <li>Author: {details.name}</li>
          <li>Comments: 0</li>
          <li>Tags: {h.getFunName()}</li>
          </ul>
        </div>
      </div>
    )
  }
});
Keys Overview

查看代码笔 宝宝第一次尝试 React,作者 Sarah Drasner (@sdras) 在 CodePen 上。

所有内容一起

既然我们理解了键,让我们花一分钟时间看看全局图景。我们讨论了 getInitialStatecomponentDidMountrender,但是 React 还有一种挂载方法。它被称为 componentWillMount。它与 componentDidMount 类似,但是它在组件首次渲染之前立即执行。

此图表并不包含 React 中所有可用的内容,而只是对我们迄今为止所涵盖内容的概览,足以让您开始动手操作。

总结

关于 React,您需要学习的内容远不止本文,这仅仅是开始。希望这篇初学者指南能帮助其他人了解如何开始使用 React。这篇文章长达 20 页,我们甚至没有涉及大量其他重要且相关的主题,包括但不限于 ES6、Browserify、Gulp、Webpack,或各种替代实现。

为了进一步学习,我强烈建议你深入学习Wes Bos 的课程,他为 CSS-Tricks 的读者提供 10% 的折扣,代码为 CSSTRICKS。另外还可以观看Frontend Masters上的视频。Michael Jackson 有一个很棒的培训课程,你可以注册(我们将在 3 月份邀请他到旧金山的 Trulia 参加活动!敬请关注报名信息)。还有 Artemij Fedosejev 写的一本很棒的书,叫做React Essentials,以及 Artem Sapegin 整理的这份资源列表,值得收藏。不要低估React 文档的价值,它也是很好的资源。祝你学习愉快!

非常感谢Michael JacksonWes BosVal Head校对本文。