React 基础(一)

Posted by Chris on March 1, 2018

扩展属性

如果你已经有了个 props 对象,并且想在 JSX 中传递它,你可以使用 … 作为扩展操作符来传递整个属性对象。下面两个组件是等效的:

function App1() {
  return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
  const props = {firstName: 'Ben', lastName: 'Hector'};
  return <Greeting {...props} />;
}

当你构建通用容器时,扩展属性会非常有用。然而,这样做也可能让很多不相关的属性,传递到不需要它们的组件中使代码变得混乱。我们建议你谨慎使用此语法。

JSX 会移除空行和开始与结尾处的空格

JSX 会移除空行和开始与结尾处的空格。标签邻近的新行也会被移除,字符串常量内部的换行会被压缩成一个空格,所以下面这些都等价:

<div>Hello World</div>

<div>
  Hello World
</div>

<div>
  Hello
  World
</div>

<div>

  Hello World
</div>

为 DOM 元素添加 Ref

React 支持给任意组件添加特殊属性。ref 属性接受一个回调函数,它在组件被加载或卸载时会立即执行。

当给 HTML 元素添加 ref 属性时,ref 回调接收了底层的 DOM 元素作为参数。例如,下面的代码使用 ref 回调来存储 DOM 节点的引用。

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.focus = this.focus.bind(this);
  }

  focus() {
    // 直接使用原生 API 使 text 输入框获得焦点
    this.textInput.focus();
  }

  render() {
    // 使用 `ref` 的回调将 text 输入框的 DOM 节点存储到 React
    // 实例上(比如 this.textInput)
    return (
      <div>
        <input
          type="text"
          ref={(input) => { this.textInput = input; }} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focus}
        />
      </div>
    );
  }
}

React 组件在加载时将 DOM 元素传入 ref 的回调函数,在卸载时则会传入 null。ref 回调会在componentDidMount 或 componentDidUpdate 这些生命周期回调之前执行。

为了在类上设置一个属性使用 ref 回调是访问 DOM 元素的常见模式。首先的方法就如上面的例子中一样设置 ref。甚至还有更简短的写法: ref={input => this.textInput = input}。

不会突变的数据的力量

避免此类问题最简单的方式是避免使用值可能会突变的属性或状态。例如,上面例子中的handleClick应该用concat重写成:

handleClick() {
  this.setState(prevState => ({
    words: prevState.words.concat(['marklar'])
  }));
}

ES6支持数组的spread语法可以让它变得更容易。如果你使用的是Create React App,那么此语法默认可用。

handleClick() {
  this.setState(prevState => ({
    words: [...prevState.words, 'marklar'],
  }));
};

你也可以用相似的方式重写可以会突变的对象。例如,假设我们有一个叫colormap的对象,我们想写一个把colormap.right改变成’blue’的函数,我们应该写:

function updateColorMap(colormap) {
  colormap.right = 'blue';
}

想要实现代码而不污染原始对象,我们可以使用Object.assign方法:

function updateColorMap(colormap) { return Object.assign({}, colormap, {right: ‘blue’}); } updateColorMap现在会返回一个新对象,而不会改变之前的旧对象。Object.assign在ES6中,需要polyfill支持。

有一个JavaScript提议来添加对象spread属性以便不会突然变化的更新对象:

function updateColorMap(colormap) {
  return {...colormap, right: 'blue'};
}

如果使用Create React App,默认情况下 Object.assign和spread对象都可以使用。

递归子节点

默认时。当递归DOM节点的子节点,React仅在同一时间点递归两个子节点列表,并在有不同时产生一个变更。

例如,当在子节点末尾增加一个元素,两棵树的转换效果很好:

<ul>
  <li>first</li>
  <li>second</li>
</ul>

<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>
React将会匹配两棵树的<li>first</li>,并匹配两棵树的<li>second</li> 节点,并插入<li>third</li>节点树。

若原生实现,在开始插入元素会使得性能更棘手。例如,两棵树的转换效果则比较糟糕:

<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

React会调整每个子节点,而非意识到可以完整保留 <li>Duke</li> 和 <li>Villanova</li> 子树。低效成了一个问题。

Keys

为解决该问题,React支持了一个key属性。当子节点有key时,React使用key来匹配原本树的子节点和新树的子节点。例如,增加一个key在之前效率不高的样例中能让树的转换变得高效:

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

现在React知道带有’2014’的key的元素是新的,并仅移动带有’2015’和’2016’的key的元素。