react基础(十三)

109 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情

前言

大家好呀,我是L同学。在上篇文章中react基础(十二)— 高阶组件的使用我们学习了react中高阶组件的知识点。今天我们学习下forwardRef的使用和Portals的使用。

forwardRef的使用

在这篇文章react基础(十一)中,我们可以通过创建ref来获取对应的DOM。使用ref推荐的方式是传入一个对象,通过React.reactRef()方式创建一个对象,使用时获取到创建的对象其中有一个current属性就是对应的元素。我们来复习下。

import React, { PureComponent, createRef } from 'react'

export default class App extends PureComponent {
  constructor(props) {
    super(props)
    this.titleRef = createRef()
  }
  render() {
    return (
      <div>
        <h2 ref={this.titleRef}>hello world</h2>
        <button onClick={e => this.printRef()}>打印ref</button>
      </div>
    )
  }

  printRef() {
    console.log(this.titleRef.current);
  }
}

点击打印ref按钮可以获取到DOM元素h2。

image.png 除了获取DOM元素,还可以通过ref获取组件。

创建一个Home组件。

class Home extends PureComponent {
  render() {
    return <h2>Home</h2>
  }
}
export default class App extends PureComponent {
  constructor(props) {
    super(props)
    this.homeRef = createRef()
  }
  render() {
    return (
      <div>
        <Home ref={this.homeRef}/>
        <button onClick={e => this.printRef()}>打印ref</button>
      </div>
    )
  }

  printRef() {
    console.log(this.homeRef.current);
  }
}

可以看到点击打印ref按钮获取到了Home组件。

image.png 在讲解ref的那篇文章中,我们说过ref不能应用于函数式组件。因为函数式组件没有实例,所以不能获取到对应的组件对象。

我们来尝试下。

首先我们创建一个函数式组件Profile。

function Profile() {
  return <p>Profile</p>
}

跟上述操作一致,通过createRef()创建profileRef对象,然后将这个对象传入Profile组件中的ref属性。

<Profile ref={this.profileRef}/>

此时,页面控制台报了一个警告。

image.png 并且点击按钮,无法获取到Profile组件。

image.png

我想要拿到Profile组件的p元素,那么有人会这样想,可以把ref作为属性,传递给子组件,然后子组件通过props能获取到吗?就像传递name属性给子组件Profile。

<Profile ref={this.profileRef} name={'haha'}/>

然后在子组件Profile中通过props进行接收。

function Profile (props) {
  console.log(props.ref);
  return <p ref={props.ref}>Profile</p>
}

我们可以看到一个警告,提示ref不是一个prop传递给下一个组件,这个ref是react内部进行管理的。

image.png

在警告提示中,我们可以看到可以通过forwardRef高阶组件来获取函数式组件中某个元素的DOM。

接下来我们通过forwardRef高阶组件来拿到Profile组件中的p元素。

在上一篇文章中,我们学习了高阶组件的定义和使用。高阶组件是一个函数,接收一个组件作为参数,并且返回一个组件。

const Profile = forwardRef(function(props, ref) {
  return <p ref={ref}>Profile</p>
})

我们可以看到点击打印ref按钮能获取到Profile组件中的p元素。

image.png

Portals的使用

在某些情况下,我们希望渲染的内容独立于父组件,甚至是独立于当前挂载到的DOM元素中(默认都是挂载到id为root的MON元素上)。

举个例子。在Home组件中有个按钮,我希望点击按钮,就能在页面中间弹出个弹窗。我们不能在Home组件中渲染弹窗,因为弹窗很大。

image.png

import React, {PureComponent} from 'react'

class Home extends PureComponent {
  render() {
    return (
      <div>
        <h2>Home</h2>
      </div>
    )
  }
}

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Home/>
      </div>
    )
  }
}

这样的需求我们可以使用Portals。Portal提供了一种将子节点渲染到存在于父组件之外的DOM节点的方法。

第一个参数child是任何可渲染的react子元素。第二个参数container是一个DOM元素。

ReactDOM.createPortal(child, container)

比如说,我们开发一个Model组件,将它的子组件渲染到屏幕中间。

首先我们在index.html中创建一个id为model的元素,用来挂载。

    <div id="model"></div>

编写这个节点的样式。

#model {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

编写组件代码。

import ReactDOM from 'react-dom'

class Model extends PureComponent {
  render() {
    return ReactDOM.createPortal(
      this.props.children,
      document.getElementById('model')
    )
  }
}

class Home extends PureComponent {
  render() {
    return (
      <div>
        <h2>Home</h2>
        <Model>
          <h2>Title</h2>
        </Model>
      </div>
    )
  }
}

image.png