首页 > 其他分享 >react官方文档-高级部分-Render Props学习(重要)

react官方文档-高级部分-Render Props学习(重要)

时间:2023-01-31 22:57:41浏览次数:91  
标签:React return Render render Component prop react Props 组件

前言:术语 “render prop” 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术

 

具有 render prop 的组件接受一个函数,该函数返回一个 React 元素并调用它而不是实现自己的渲染逻辑。

<DataProvider render={data => (
  <h1>Hello {data.target}</h1>
)}/>

使用 render prop 的库有 React RouterDownshift 以及 Formik

在这个文档中,我们将讨论为什么 render prop 是有用的,以及如何写一个自己的 render prop 组件。

使用 Render Props 来解决横切关注点(Cross-Cutting Concerns)

组件是 React 代码复用的主要单元,但如何分享一个组件封装到其他需要相同 state 组件的状态或行为并不总是很容易。

例如,以下组件跟踪 Web 应用程序中的鼠标位置:

class MouseTracker 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: '100vh' }} onm ouseMove={this.handleMouseMove}>
        <h1>移动鼠标!</h1>
        <p>当前的鼠标位置是 ({this.state.x}, {this.state.y})</p>
      </div>
    );
  }
}

当光标在屏幕上移动时,组件在 <p> 中显示其(x,y)坐标。

现在的问题是:我们如何在另一个组件中复用这个行为?换个说法,若另一个组件需要知道鼠标位置,我们能否封装这一行为,以便轻松地与其他组件共享它??

由于组件是 React 中最基础的代码复用单元,现在尝试重构一部分代码使其能够在 <Mouse> 组件中封装我们需要共享的行为。

// <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: '100vh' }} onm ouseMove={this.handleMouseMove}>

        {/* ...但我们如何渲染 <p> 以外的东西? */}
        <p>The current mouse position is ({this.state.x}, {this.state.y})</p>
      </div>
    );
  }
}

class MouseTracker extends React.Component {
  render() {
    return (
      <>
        <h1>移动鼠标!</h1>
        <Mouse />
      </>
    );
  }
}

现在 <Mouse> 组件封装了所有关于监听 mousemove 事件和存储鼠标 (x, y) 位置的行为,但其仍不是真正的可复用。

举个例子,假设我们有一个 <Cat> 组件,它可以呈现一张在屏幕上追逐鼠标的猫的图片。我们或许会使用 <Cat mouse={{ x, y }} prop 来告诉组件鼠标的坐标以让它知道图片应该在屏幕哪个位置。

首先, 你或许会像这样,尝试在 <Mouse> 内部的渲染方法渲染 <Cat> 组件::

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: '100vh' }} onm ouseMove={this.handleMouseMove}>

        {/*
          我们可以在这里换掉 <p> 的 <Cat>   ......
          但是接着我们需要创建一个单独的 <MouseWithSomethingElse>
          每次我们需要使用它时,<MouseWithCat> 是不是真的可以重复使用.
        */}
        <Cat mouse={this.state} />
      </div>
    );
  }
}

class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>移动鼠标!</h1>
        <MouseWithCat />
      </div>
    );
  }
}

这种方法适用于我们的特定用例,但我们还没有达到以可复用的方式真正封装行为的目标。现在,每当我们想要鼠标位置用于不同的用例时,我们必须创建一个新的组件(本质上是另一个 <MouseWithCat> ),它专门为该用例呈现一些东西.

这也是 render prop 的来历:我们可以提供一个带有函数 prop 的 <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 }} />
    );
  }
}

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: '100vh' }} onm ouseMove={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>移动鼠标!</h1>
        <Mouse render={mouse => (
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}

现在,我们提供了一个 render 方法 让 <Mouse> 能够动态决定什么需要渲染,而不是克隆 <Mouse> 组件然后硬编码来解决特定的用例。

更具体地说,render prop 是一个用于告知组件需要渲染什么内容的函数 prop。

这项技术使我们共享行为非常容易。要获得这个行为,只要渲染一个带有 render prop 的 <Mouse> 组件就能够告诉它当前鼠标坐标 (x, y) 要渲染什么。

关于 render prop 一个有趣的事情是你可以使用带有 render prop 的常规组件来实现大多数高阶组件 (HOC)。 例如,如果你更喜欢使用 withMouse HOC而不是 <Mouse> 组件,你可以使用带有 render prop 的常规 <Mouse> 轻松创建一个:

// 如果你出于某种原因真的想要 HOC,那么你可以轻松实现
// 使用具有 render prop 的普通组件创建一个!
function withMouse(Component) {
  return class extends React.Component {
    render() {
      return (
        <Mouse render={mouse => (
          <Component {...this.props} mouse={mouse} />
        )}/>
      );
    }
  }
}

因此,你可以将任一模式与 render prop 一起使用。

使用 Props 而非 render

重要的是要记住,render prop 是因为模式才被称为 render prop ,你不一定要用名为 render 的 prop 来使用这种模式。事实上, 任何被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 “render prop”.

尽管之前的例子使用了 render,我们也可以简单地使用 children prop!

<Mouse children={mouse => (
  <p>鼠标的位置是 {mouse.x},{mouse.y}</p>
)}/>

记住,children prop 并不真正需要添加到 JSX 元素的 “attributes” 列表中。相反,你可以直接放置到元素的内部

<Mouse>
  {mouse => (
    <p>鼠标的位置是 {mouse.x},{mouse.y}</p>
  )}
</Mouse>

你将在 react-motion 的 API 中看到此技术。

由于这一技术的特殊性,当你在设计一个类似的 API 时,你或许会要直接地在你的 propTypes 里声明 children 的类型应为一个函数。

Mouse.propTypes = {
  children: PropTypes.func.isRequired
};

注意事项

将 Render Props 与 React.PureComponent 一起使用时要小心

如果你在 render 方法里创建函数,那么使用 render prop 会抵消使用 React.PureComponent 带来的优势。因为浅比较 props 的时候总会得到 false,并且在这种情况下每一个 render 对于 render prop 将会生成一个新的值。

例如,继续我们之前使用的 <Mouse> 组件,如果 Mouse 继承自 React.PureComponent 而不是 React.Component,我们的例子看起来就像这样:

class Mouse extends React.PureComponent {
  // 与上面相同的代码......
}

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

        {/*
          这是不好的!
          每个渲染的 `render` prop的值将会是不同的。
        */}
        <Mouse render={mouse => (
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}

在这样例子中,每次 <MouseTracker> 渲染,它会生成一个新的函数作为 <Mouse render> 的 prop,因而在同时也抵消了继承自 React.PureComponent 的 <Mouse> 组件的效果!

为了绕过这一问题,有时你可以定义一个 prop 作为实例方法,类似这样:

class MouseTracker extends React.Component {
  // 定义为实例方法,`this.renderTheCat`始终
  // 当我们在渲染中使用它时,它指的是相同的函数
  renderTheCat(mouse) {
    return <Cat mouse={mouse} />;
  }

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

如果你无法静态定义 prop(例如,因为你需要关闭组件的 props 和/或 state),则 <Mouse> 应该扩展 React.Component

 

 

标签:React,return,Render,render,Component,prop,react,Props,组件
From: https://www.cnblogs.com/zccst/p/17081090.html

相关文章

  • react 高效高质量搭建后台系统 系列 —— 系统布局
    其他章节请看:react高效高质量搭建后台系统系列系统布局前面我们用脚手架搭建了项目,并实现了登录模块,登录模块所依赖的请求数据和antd(ui框架和样式)也已完成。本篇将......
  • react native启动时报错Could not resolve com.facebook.react:react-native:+
    启动项目是报错大致如下:解决这个issue已经给出了解决方法https://github.com/facebook/react-native/issues/35210rn>=0.63rn官方为大于0.63的所有主要版本都准......
  • react native启动项目报错Received status code 403 from server: Forbidden
    问题:启动项目时报错如下图:解决:点击报错中的.xml链接,浏览器中可以正常下载,意识到大概率是代理的问题。国内开发环境一直是一个比较大问题,开发得一直连着代理,需要去an......
  • 谈谈Vue3中的ref和reactive
    一、是什么?ref和reactive是Vue3中用来实现数据响应式的API一般情况下,ref定义基本数据类型,reactive定义引用数据类型(我喜欢用它来定义对象,不用它定义数组,原因后面讲)我......
  • react官方文档-高级部分-性能优化学习
    前言:UI更新需要昂贵的DOM操作,而React内部使用几种巧妙的技术以便最小化DOM操作次数。对于大部分应用而言,使用React时无需专门优化就已拥有高性能的用户界面。尽管......
  • react官方文档-高级部分-深入JSX学习
    前言:jsx好多用法,现在还第一次使用。实际上,JSX仅仅只是 React.createElement(component,props,...children) 函数的语法糖。 指定React元素类型JSX标签的第一......
  • react官方文档-高级部分-高阶组件学习
    前言:解释了redux实现原理,及避免一些坑,比如静态方法和ref不能传下去。 高阶组件(HOC)是React中用于复用组件逻辑的一种高级技巧。HOC自身不是ReactAPI的一部分,它是一......
  • React组件的使用
    React组件简述组件是React的一等公民,使用React就是在用组件。组件是整个页面中的一小块,把这一小块抽成独立的,可复用的,UI小部件。一个页面有多个组件构成,组件可复用,可以......
  • react useContext
    一、什么是useContext在Reactclass式中父组件向子组件传递参数可以通过props,context。但是在函数式组件中需要向多层组件传递数据时,此时就可以使用useContext/二、......
  • 玩转web3第一篇——web3-react
    概况web3-react是由NoahZinsmeister开发的一个web3框架,主要功能是实时获取DApp里的关键数据(如用户当前连接的地址、网络、余额等)。Noah也是著名的去中心化交易所uniswap......