美文网首页
[蹭热度系列]React的一些新特性

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

作者: 李天_skyzjy | 来源:发表于2018-03-06 21:53 被阅读82次

    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)了

    相关文章

      网友评论

          本文标题:[蹭热度系列]React的一些新特性

          本文链接:https://www.haomeiwen.com/subject/ghmrfftx.html