React 重温之Render Props

背景

React 16+ 版本更新后,新增了许多有意思的功能,做为一个苦逼哈哈的前端,也只能与时俱进的继续学习学习了。前端

什么是 Render Props

先看官方是怎么说的:函数

The term “render prop” refers to a simple technique for sharing code between React components using a prop whose value is a function.

翻译成大白话就是:这个技术呢,很简单,就是给组件添加一个值为函数的属性,这个函数能够在组件渲染(render)的时候调用,那这个组件是干啥用的呢?就是为了给原有组件“注入”其它组件的代码。性能

为啥要有这个东西?

看完官方的说法,想必是一头雾水,为啥要经过这么麻烦的方式给一个组件“注入”另外一个组件的代码?React不是本来就支持组件嵌套和传值吗?学习

人官方网站也说了,这个技术只是用来解决一些比较特殊的问题的,并非让你没事就用的, 那么咱们可能就要问了,何时要用呢?动画

一样套用官方的说法:网站

a render prop is a function prop that a component uses to know what to render.

翻译过来就是说,这个渲染属性就是让组件知道本身渲染什么东西。。。 这不废话嘛!别急,还有大白话翻译呢。this

大白话翻译过来就是说啊:若是你一个组件不知道本身渲染什么东西,或者说你一个组件的基础功能是提供”可变数据源“,具体展现UI能够从外部注入,那么就能够用这个技术了。 是否是仍是一脸懵?一头雾?不要紧,看个官方例子好了。翻译

官方教程解释

可变数据源组件

首先是一个Mouse组件,能够实时获取鼠标位置并展现出来,那么在这里,这个Mouse组件提供”可变数据源“,也就是跟随鼠标一直变化的鼠标位置。code

那你在页面渲染个鼠标位置有什么用啊?谁没事儿盯着你页面看鼠标位置啊?可是咱们可用鼠标位置作许多用意思的东西啊,好比说跟随鼠标移动的动画啊,拖动页面元素移动啊什么的。。。component

在这里,咱们说Mouse组件提供“可变数据源”,可是它并不知道本身要渲染什么,它只是一个基础数据的提供者

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}>

        {/* ...but how do we render something other than a <p>? */}
        <p>The current mouse position is ({this.state.x}, {this.state.y})</p>
        {this.props.render(this.state)} // 这个比较关键
      </div>
    );
  }
}

咱们看到在组件的渲染方法(render)里,没什么有价值的东西,但有一行代码很关键{this.props.render(this.state)}, 这是什么意思?

很简单,调用Mouse组件的props里的render函数(这个render就是咱们前面说的 “一个值为函数的属性” ,叫啥名儿都行,叫render是凑巧了,谁让这个技术起码是Render Props呢)来往这里渲染UI,Mouse只负责把“可变数据源”做为参数给render函数(props里面的属性函数,可不是声明周期的render函数)就行了。至于渲染的内容是什么?那就不是Mouse组件能控制了。

被注入UI组件

而后就是一个使用数据源的组件Cat了,让Cat图片跟随鼠标位置的变化而移动。怎么变?固然是使用Mouse组件提供的可变数据源了。

这个Cat组件就是咱们但愿渲染在页面的UI了。之后可能会有dog、pig之类的,都须要用到Mouse提供的基础数据,也就是“可变数据源”。可是Cat并不能获取Mouse的数据变更啊,怎么把这俩绑定(联系)在一块儿呢?

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 }} />
    );
  }
}

绑定?仍是调用吧

而后就是绑定的出场了,说绑定有点不太合适,说调用可能更符合实际状况一点,以前咱们在Mouse的render函数(生命周期函数)里调用了组件Props上的render属性函数(自定义的,叫啥名儿都行),那么咱们在用到Mouse组件的时候,给Mouse组件一个render属性函数,这个函数使用参数(鼠标位置,经过Mouse组件的state传过来)来初始化Cat组件,而后返回。那么这样一来Cat组件就内嵌在Mouse组件的render函数里了。

也便是说一直到这个时候,咱们才知道要往Mouse组件里渲染什么UI。

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

啥时候用

放心,通常状况这个技术用是用不上的,这辈子都不可能用上。。。但还有不通常状况呢不是吗?

简单来讲,当两个平级组件之间须要单向依赖的时候,就能够用这个技术了。

那什么是平级依赖呢? 很简单,就是连个同级组件A、B,A组件须要跟随B组件的内部状态来改变本身的内部状态,咱们就说A依赖B。为何是单向依赖呢? 双向依赖怎么办? 双向依赖就把状态维护交给父组件了,谁还那么麻烦啊!

好难啊,能不能不用

固然能够不用了,这技术就不鼓励使用,那有没有别的方式能够避免使用这个技术呢? 固然能够了,单向依赖只是双向依赖的一种简单子集,把须要依赖的状态提到父组件管理就行了,只是麻烦一点而已。

one more thing

若是使用render Props技术的组件(也就是有一个render函数属性的组件)是继承自React.PureComponent 的,那么在props的浅比较看来,新老props老是不同的,那么就会每次都从新渲染,性能开销比较大。

因此推荐用法以下:

class MouseTracker extends React.Component {
  // Defined as an instance method, `this.renderTheCat` always
  // refers to *same* function when we use it in render
  renderTheCat(mouse) {
    return <Cat mouse={mouse} />;
  }

  render() {
    return (
      <div>
        <h1>Move the mouse around!</h1>
        <Mouse render={this.renderTheCat} />
      </div>
    );
  }
}

继承React.Component,并使用类函数

相关文章
相关标签/搜索