网络埋伏纪事

React 初学者教程 9:从数据到 UI

网络埋伏纪事 · 2016-10-27翻译 · 1088阅读 原文链接

简介:利用 JSX 的灵活性,将烦人的数据转化为甜蜜的 UI 元素。

在创建应用时,术语 props、state、组件、JSX 标记、render 方法以及其它 React 主义也许是你脑袋中最后考虑的事情。大部分时间,你是处理 JSON 对象、数组以及其它数据结构形式的数据,这些数据与 React 或者界面无关。跨越数据和最终看到的结果之间的鸿沟可能是令人沮丧的!但是不要担心。本文会通过将你将会遇到的一些常见场景都过一遍,来帮助减少这些令人沮丧的时刻。

示例

为帮助弄清楚你将要看到的所有事情,我们需要一个示例。这也没什么复杂的,所以继续,创建一个新的 HTML 文档,写入如下代码:

<!DOCTYPE html>
<html>

<head>
  <title>React! React! React!</title>
  <script src="https://fb.me/react-15.0.0-rc.2.js"></script>
  <script src="https://fb.me/react-dom-15.0.0-rc.2.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>

  <style>
    #container {
      padding: 50px;
      background-color: #FFF;
    }
  </style>
</head>

<body>
  <div id="container"></div>
  <script type="text/babel">
    var Circle = React.createClass({
      render: function() {
          var circleStyle = {
            padding: 10,
            margin: 20,
            display: "inline-block",
            backgroundColor: this.props.bgcolor,
            borderRadius: "50%",
            width: 100,
            height: 100,
          };

          return (
            <div style={circleStyle}>
            </div>
          );
        }
    });

    var destination = document.querySelector("#container");

    ReactDOM.render(
      <div>
        <Circle bgcolor="#F9C240"/>
      </div>,
      destination
    );
  </script>
</body>

</html>

文档写完后,在浏览器中预览一下。一切顺利的话,会看到如下的结果:

现在我们花点时间理解一下这个示例干的是什么。你看到的大部分出自 Circle 组件:

var Circle = React.createClass({
  render: function() {
      var circleStyle = {
        padding: 10,
        margin: 20,
        display: "inline-block",
        backgroundColor: this.props.bgColor,
        borderRadius: "50%",
        width: 100,
        height: 100,
      };

      return (
        <div style={circleStyle}>
        </div>
      );
    }
});

大部分代码是 circleStyle 对象,该对象包含将 div 变成圆的内联样式属性。所有样式值都是硬编码的,除了 backgroundColor 属性,它的值来自传进来的 bgColor 属性。

除了组件,最终把圆显示出来,是通过 ReactDOM.render 方法:

ReactDOM.render(
  <div>
    <Circle bgColor="#F9C240"/>
  </div>,
  destination
);

这里我们声明了 Circle 组件的一个实例,并且用 bgColor 属性设置其颜色。现在,让 Circle 组件定义在 render 方法中有一点限制 - 特别是如果你打算处理会影响 Circle 组件的数据。在下面两个小节,我们会看到解决这个问题的方法。

JSX 可以在任何地方

《深入 JSX》 教程中,我们知道 JSX 实际上是可以放在 render 函数之外,并且可以用作为值,赋值给变量或者属性。例如,我们可以勇敢地像这样做:

var theCircle = <Circle bgColor="<span class="brush:js;html-script:true">#F9C240</span>"/>

ReactDOM.render(
  <div>
    {theCircle}
  </div>,
  destination
);

theCircle 变量存储 JSX, 以实例化 Circle 组件。在 ReactDOM.render 函数中计算这个变量,结果会显示一个圆。最终结果与我们前面的示例没有什么不同,但是让 Circle 组件的实例化从 render 方法的束缚中脱离出来,给了我们更多的选择来做一些疯狂而好玩的事情。

例如,你可以进一步创建一个返回 Circle 组件的函数:

function showCircle() {
  var colors = ["#393E41", "#E94F37", "#1C89BF", "#A1D363"];
  var ran = Math.floor(Math.random() * colors.length);

  // return a Circle with a randomly chosen color
  return <Circle bgColor={colors[ran]}/>;
};

在这里,showCircle 函数返回一个Circle 组件,该组件的 bgColor 值被设置为一个随机颜色值(棒极了!)。为了让我们的示例使用 showCircle 函数,我们只需要在 ReactDOM.render 内对它求值即可:

ReactDOM.render(
  <div>
    {showCircle()}
  </div>,
  destination
);

只要求值的表达式返回 JSX,你就可以在 大括号中放入你想要的很多东西。这种灵活性很好,因为当你的 JavaScript 在 render 函数外生存时,你可以做很多事情。很多事情!

处理数组

现在我们开始做点有趣的事情。当你想显示多个组件时,你不能总是像这样手动指定它们:

ReactDOM.render(
  <div>
    {showCircle()}
    {showCircle()}
    {showCircle()}
  </div>,
  destination
);

在很多真实场景下,要显示的组件的数量与在一个数组或者类数组(比如迭代器)对象中的条目数量有关。这带来一些简单的并发症(复杂性)。例如,假如我们有如下 colors 数组:

var colors = ["#393E41", "#E94F37", "#1C89BF", "#A1D363", 
              "#85FFC7", "#297373", "#FF8552", "#A40E4C"];

我们想为这个数组中的每个条目创建一个 Circle 组件,并把 bgColor 属性设置为每个数组条目的值。为实现这个目的,我们打算创建一个 Circle 组件数组:

var colors = ["#393E41", "#E94F37", "#1C89BF", "#A1D363",
              "#85FFC7", "#297373", "#FF8552", "#A40E4C"];

var renderData = [];

for (var i = 0; i < colors.length; i++) {
  renderData.push(<Circle bgColor={colors[i]}/>);
}

在这段代码中,我们用 Circle 组件填充 renderData 数组,就像开始我们所做的一样。要显示所有这些组件,在 React 中就很简单。我们看看如下代码行:

var colors = ["#393E41", "#E94F37", "#1C89BF", "#A1D363",
              "#85FFC7", "#297373", "#FF8552", "#A40E4C"];

var renderData = [];

for (var i = 0; i < colors.length; i++) {
  renderData.push(<Circle bgColor={colors[i]}/>);
}

ReactDOM.render(
  <div>
    {renderData}
  </div>,
  destination
);

render 方法中,我们把 renderData 数组指定为需要求值的表达式。要实现如下效果,我们不需要采用任何其它步骤:

好吧,我说谎了。这里实际上我们还需要做一件事情,并且是很不容易察觉的事情。React 之所以让 UI 更新很快,是通过清楚地了解 DOM 中到底发生了什么。它用几种方式去了解,但是真正值得关注的一个方式是,通过内部用某种标识符标记每个元素。

当动态创建元素时(比如我们在 Circle 组件数组中所做),这些标识符不是自动设置的。我们需要做一些额外的工作。这种额外的工作采用 key 属性的形式,React 用 key 属性的值来唯一地识别每个特殊的组件。

对于我们的示例,我们可以像这样做:

for (var i = 0; i < colors.length; i++) {
  var color = colors[i];
  renderData.push(<Circle key={i + color} bgColor={color}/>);
}

在每个组件上,我们指定 key 属性,并将其值设置为 colors 数组内的 color 和索引位置的组合。这确保我们动态创建的每个组件最终有一个唯一的标识符,随后 React 就可以用这个唯一的标识符来优化任何将来的 UI 更新。

检查你的控制台!

React 相当擅长当你做错了事时告诉你。例如,如果你动态创建元素/组件,但是没有为它们指定一个 key 属性,你会在控制上中得到如下警告:

Warning: Each child in an array or iterator should have a unique "key" prop. Check the top-level render call using \.

使用 React 时,周期性地检查控制台消息是个好主意。

总结

在本文中你所看到的所有诀窍都可能是由一个东西造成:JSX 就是 Javascript。这就是为什么 JavaScript 出现的地方都可以用 JSX 的原因。对于我们来说,下面这样的代码看起来是相当怪异的:

for (var i = 0; i < colors.length; i++) {
  var color = colors[i];
  renderData.push(<Circle key={i + color} bgColor={color}/>);
}

尽管我们把一些 JSX 判断压到一个数组中,像变戏法一样,当 renderDatarender 方法内被求值时,所有代码都能正常运行。我讨厌听起来像坏唱片一样,但是这是因为我们的浏览器最终看起来像这样子:

for (var i = 0; i < colors.length; i++) {
  var color = colors[i];

  renderData.push(React.createElement(Circle, 
    { 
      key: i + color,
      bgColor: color 
    }));
}

当 JSX 被转换为纯 JS 时,一切又变得有意义。这就是为什么我们可以将 JSX 和 数据一起放在各种不舒服的条件下,但是依然可以得到我们想要的最终结果的原因。因为,最终,都是 JavaScript。

相关文章