[蹭热度系列]React的一些新特性

2018-03-06  本文已影响82人  李天_skyzjy

React 16其实已经出来了大半年了,作为一个前端从业者,深深为自己的技术迟钝感到愧疚。
最近同事讨论的比较多,好奇心驱使下看了下react的文档,现学现卖,分享如下:(示例代码大部分摘抄自React Posts, 原文链接)

1. Fragment

Fragment 是 16.0加入的新特性,在16.2中获得了升级。
在之前的React中,如果我们要在一个Component里面render诸如以下的view

Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.

那么我们只能用下面的方法:

render() {
  return (
    <div>
      Some text.
      <h2>A heading</h2>
      More text.
      <h2>Another heading</h2>
      Even more text.
    </div>
  );
}

这样导致的问题是,无论我们需不需要,render出的真正的HTML DOM上会多出一个<div>或者<span>
为了解决这个问题,React16.0提供了在render方法中返回array的功能,现在代码可以写成这样:

render() {
 return [
  "Some text.",
  <h2 key="heading-1">A heading</h2>,
  "More text.",
  <h2 key="heading-2">Another heading</h2>,
  "Even more text."
 ];
}

然而我们还是不满意,这个写法太不JSX了,所以提供了Fragment这个Component来解决问题(所以你需要import {Fragment} from 'react'啦)

render() {
  return (
    <Fragment>
      Some text.
      <h2>A heading</h2>
      More text.
      <h2>Another heading</h2>
      Even more text.
    </Fragment>
  );
}

还有更简单的写法(为了编译通过你可能需要一些babel的支持)

render() {
  return (
    <>
      Some text.
      <h2>A heading</h2>
      More text.
      <h2>Another heading</h2>
      Even more text.
    </>
  );
}

这样我们真正render出的HTML DOM结构里就不存在不需要的<div>或者<span>了


不用<Fragment> Render出的DOM 使用<Fragment> Render出的DOM

2. Render Props

Render Props可以作为一种新的HOC的写法(见下例),官方给的例子是一个track鼠标位置的component和一个show图片的component:

class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse
    return (
      <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
    );
  }
}

class MouseWithCat extends React.Component {
  constructor(props) {
    super(props);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.state = { x: 0, y: 0 };
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>

        {/*
          We could just swap out the <p> for a <Cat> here ... but then
          we would need to create a separate <MouseWithSomethingElse>
          component every time we need to use it, so <MouseWithCat>
          isn't really reusable yet.
        */}
        <Cat mouse={this.state} />
      </div>
    );
  }
}

class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>Move the mouse around!</h1>
        <MouseWithCat />
      </div>
    );
  }
}

定义了MouseWithCat这样一个Component来 Handle Mouse Tracking 这一事件,把track到的x,y坐标放到下一层Cat的mouse这个props上,这是我们之前的一种写法。

现在拥有了Render Props后,我们可以这样写:

class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse;
    return (
      <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
    );
  }
}

class Mouse extends React.Component {
  constructor(props) {
    super(props);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.state = { x: 0, y: 0 };
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>

        {/*
          Instead of providing a static representation of what <Mouse> renders,
          use the `render` prop to dynamically determine what to render.
        */}
        {this.props.render(this.state)}
      </div>
    );
  }
}

class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>Move the mouse around!</h1>
        <Mouse render={mouse => (
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}

注意Mouse的Render方法中调用的this.props.render, 和我们在MouseTracker中传给Mouse的render方法。
代码展开一下,实际上是我们在Mouse的render方法中调用了我们给Mouse.props传递的render方法,这个render方法接受一个mouse参数,并且返回一个Cat Component, 这个Cat Component接受mouse这个参数,并且在鼠标显示的位置显示一张图片。

使用这个特性的好处很明显,我们不需要MouseWithCat这个Component了,现在Mouse和Cat是两个独立的component,而且我们可以给Mouse传入其他的render参数,比如<Mouse render={mouse=>(<Doge mouse={mouse}></Doge>)}>,而不用再创建一个MouseWithDog

但这个特性其实和这种写法效果相似:

const withMouse = (Component) => ({mouse}) {
  //some code
  return <Component  mouse={mouse} />
}

调用的时候我们先创建一个MouseWithCat变量:

const MouseWithCat = withMouse(Cat);

<MouseWithCat mouse={mouse} />

在效果上来说,都把Mouse和Cat这两个Component解绑了,但有了render props支持后,写法更简洁了。
另外withMouse这个Higher Order Component,也可以使用Render Props:

function withMouse(Component) {
  return class extends React.Component {
    render() {
      return (
        <Mouse render={mouse => (
          <Component {...this.props} mouse={mouse} />
        )}/>
      );
    }
  }
}

Render Props的render和我们Component的render是两个不同的东西,不要弄混
了。实际上,只是这种写法叫做Render props, 这里只是为了方便理解起了个名字叫render, 我们还可以叫它abc,在Component中调用的时候使用this.props.abc

叫children也可以。

<Mouse children={mouse => (
  <p>The mouse position is {mouse.x}, {mouse.y}</p>
)}/>

再简化:

<Mouse>
  {mouse => (
    <p>The mouse position is {mouse.x}, {mouse.y}</p>
  )}
</Mouse>

这就是一种FACC(Function As Child Component)了

上一篇 下一篇

猜你喜欢

热点阅读