【React系列】Portals、Fragment

本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg5MDAzNzkwNA==&action=getalbum&album_id=1566025152667107329)

Portals

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

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案:

javascript 复制代码
ReactDOM.createPortal(child, container)
  • 第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment
  • 第二个参数(container)是一个 DOM 元素;

通常来讲,当你从组件的 render 方法返回一个元素时,该元素将被挂载到 DOM 节点中离其最近的父节点:

javascript 复制代码
render() {
  // React 挂载了一个新的 div,并且把子元素渲染其中
  return (
    <div>      
      {this.props.children}
    </div>  
  );
}

然而,有时候将子元素插入到 DOM 节点中的不同位置也是有好处的:

javascript 复制代码
render() {
  // React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
  // `domNode` 是一个可以在任何位置的有效 DOM 节点。
  return ReactDOM.createPortal(
    this.props.children,
    domNode  
  );
}

比如说,我们准备开发一个 Modal 组件,它可以将它的子组件渲染到屏幕的中间位置:

步骤一:修改index.html添加新的节点

html 复制代码
<div id="root"></div>
<!-- 新节点 -->
<div id="modal"></div>

步骤二:编写这个节点的样式:

css 复制代码
#modal {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background-color: red;
}

步骤三:编写组件代码

javascript 复制代码
import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';

class Modal extends PureComponent {
  constructor(props) {
    super(props);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      document.getElementById("modal")
    )
  }
}

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Modal>
          <h2>我是标题</h2>
        </Modal>
      </div>
    )
  }
}

第三方库中的Modal,如AntDesign,通常都是通过主动创建一个divdocument.createElement),然后调用ReactDOM.render()方法来渲染到自己创建的div上的。

Fragment

在之前的开发中,我们总是在一个组件中返回内容时包裹一个div元素:

javascript 复制代码
export default class App extends PureComponent {
  render() {
    return (
      <div>
        <h2>当前计数: 0</h2>
        <button>+1</button>
        <button>-1</button>
      </div>
    )
  }
}

我们会发现多了一个div元素:

  • 这个div元素对于某些场景是需要的(比如我们就希望放到一个div元素中,再针对性设置样式)
  • 某些场景下这个div是没有必要的,比如当前这里我可能希望所有的内容直接渲染到root中即可;

我们可以删除这个div吗?

我们又希望可以不渲染这样一个div应该如何操作呢?

  • 使用Fragment
  • Fragment 允许你将子列表分组,而无需向 DOM 添加额外节点;

React还提供了Fragment的段语法:

  • 它看起来像空标签 <> </>
javascript 复制代码
export default class App extends PureComponent {
  render() {
    return (
      <>
        <h2>当前计数: 0</h2>
        <button>+1</button>
        <button>-1</button>
      </>
    )
  }
}

但是,如果我们需要在Fragment中添加key,那么就不能使用段语法:

javascript 复制代码
{
  this.state.friends.map((item, index) => {
    return (
      <Fragment key={item.name}>
        <div>{item.name}</div>
        <div>{item.age}</div>
      </Fragment>
    )
  })
}

这里是不支持如下写法的:

javascript 复制代码
<key={item.name}>
  <div>{item.name}</div>
  <div>{item.age}</div>
</>

StrictMode

StrictMode 是一个用来突出显示应用程序中潜在问题的工具。

  • Fragment 一样,StrictMode 不会渲染任何可见的 UI;
  • 它为其后代元素触发额外的检查和警告;
  • 严格模式检查仅在开发模式下运行;它们不会影响生产构建;

可以为应用程序的任何部分启用严格模式:

javascript 复制代码
import React from 'react';

function ExampleApplication() {
  return (
    <div>
      <Header />
      <React.StrictMode>
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>
      <Footer />
    </div>
  );
}
  • 不会对 HeaderFooter 组件运行严格模式检查;
    但是,ComponentOneComponentTwo 以及它们的所有后代元素都将进行检查;

但是检测,到底检测什么呢?

  1. 识别不安全的生命周期:
javascript 复制代码
class Home extends PureComponent {
  UNSAFE_componentWillMount() {

  }

  render() {
    return <h2>Home</h2>
  }
}
  1. 使用过时的 ref API
javascript 复制代码
class Home extends PureComponent {
  UNSAFE_componentWillMount() {

  }

  render() {
    return <h2 ref="home">Home</h2>
  }
}
  1. 使用废弃的findDOMNode方法

在之前的React API中,可以通过findDOMNode来获取DOM,不过已经不推荐使用了,可以自行学习演练一下

  1. 检查意外的副作用
javascript 复制代码
class Home extends PureComponent {
  constructor(props) {
    super(props);

    console.log("home constructor");
  }

  UNSAFE_componentWillMount() {

  }

  render() {
    return <h2 ref="home">Home</h2>
  }
  • 这个组件的constructor会被调用两次;
  • 这是严格模式下故意进行的操作,让你来查看在这里写的一些逻辑代码被调用多次时,是否会产生一些副作用;
  • 在生产环境中,是不会被调用两次的;
  1. 检测过时的context API

早期的Context是通过static属性声明Context对象属性,通过getChildContext返回Context对象等方式来使用Context的;

目前这种方式已经不推荐使用,大家可以自行学习了解一下它的用法;

相关推荐
Python私教1 天前
FastAPI+React19 ERP系统实战 第01期
react·fastapi
一笑code3 天前
vue/微信小程序/h5 实现react的boundary
微信小程序·vue·react
ZHOU_WUYI15 天前
GitHub OAuth 认证示例
flask·github·react
SuperherRo15 天前
Web攻防-XSS跨站&浏览器UXSS&突变MXSS&Vue&React&Electron框架&JQuery库&写法和版本
vue.js·electron·jquery·react·xss·mxss·uxss
飞鸟malred17 天前
nextjs入门
前端·react·nextjs
aiguangyuan21 天前
React 动态路由的使用和实现原理
react·前端开发
aiguangyuan21 天前
手写简版React-router
react·前端开发
aiguangyuan22 天前
React-router实现原理剖析
react·前端开发
aiguangyuan22 天前
React 19 新特性
react·前端开发
亦世凡华、22 天前
React--》使用vite构建器打造高效的React组件库
经验分享·react·组件库·组件库开发