react基础(十四)— fragment和strictMode

209 阅读4分钟

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

前言

大家好呀,我是L同学。在上篇文章中react基础(十三)我们学习了react中的forwardRef的使用和Portals的使用。今天我们学习fragment和strictMode。

fragment

我们通过计数器这个案例来讲解fragment。

import React, {PureComponent} from 'react'

export default class App extends PureComponent {
  constructor() {
    super()
    this.state = {
      counter: 0
    }
  }
  render() {
    return (
      <div>
        <h2>当前计数: {this.state.counter}</h2>
        <button onClick={e => this.increment()}>+1</button>
      </div>
    )
  }

  increment() {
    this.setState({
      counter: this.state.counter + 1
    })
  }
}

我们可以看到h2元素和按钮外面有一层div元素。 image.png 如果不想要这层div元素,可以直接把它去掉吗?答案是不可以的,去掉会报错,在jsx中只能有一个根元素。

image.png 那么我不想要渲染这个没有用的外层div元素,该怎么办呢?

此时可以使用Fragment。Fragment允许你将子列表分组,不需要向DOM添加额外节点。

  render() {
    return (
      <React.Fragment>
        <h2>当前计数: {this.state.counter}</h2>
        <button onClick={e => this.increment()}>+1</button>
      </React.Fragment>
    )
  }

我们也可以这样写。先从react中导入。

import React, { PureComponent, Fragment } from 'react'
      <Fragment>
        <h2>当前计数: {this.state.counter}</h2>
        <button onClick={e => this.increment()}>+1</button>
      </Fragment> 

此时h2元素和按钮外层没有渲染多余的标签。 image.png 在react中,还提供了Fragment的短语法,它看起来箱空标签<></>。

      <>
        <h2>当前计数: {this.state.counter}</h2>
        <button onClick={e => this.increment()}>+1</button>
      </>

同样,可以正常渲染。

image.png 接下来,我们渲染数组。

    this.state = {
      counter: 0,
      friends: [
        {name: 'haha', age: 18},
        {name: 'xixi', age: 20},
        {name: 'lili', age: 22}
      ]
    }
      <>
        <h2>当前计数: {this.state.counter}</h2>
        <button onClick={e => this.increment()}>+1</button>
        <div>
          {
            this.state.friends.map((item, index) => {
              return (
                <>
                <div>{item.name}</div>
                <p>{item.age}</p>
                <hr/>
                </>
              )
            }) 
          }
        </div>
      </>

我们可以看到它们的结构。

image.png 在渲染数组时,我们通常会添加key来进行性能优化。

需要注意的是,如果我们需要在Fragment中添加key,那么就不能使用短语法。

我们只能这么写。

<div>
          {
            this.state.friends.map((item, index) => {
              return (
                <Fragment key={item.name}>
                <div>{item.name}</div>
                <p>{item.age}</p>
                <hr/>
                </Fragment>
              )
            }) 
          }
        </div>

StrictMode

StrictMode是一个用来突出显示应用程序中潜在问题的工具。它和Fragment一样不会渲染任何可见的UI。StrictMode为它的后代元素触发额外的检查和警告。严格模式检查仅在开发模式下运行,它们不会影响生产构建。

StrictMode可以为应用程序的任何部分开启严格模式。在以下代码中,会对Home组件以及它的所有后代元素都进行检查。但是不会对Profile组件进行严格模式检查。

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

严格模式检查检查的是什么呢?接下来我们对进行严格检查的Home组件和不进行严格检查的Profile组件进行比较。

1. 识别不安全的生命周期

我们在Home组件和Profile组件中使用componentWillMount生命周期函数。

Home组件。

class Home extends PureComponent {
  componentWillMount() {
    console.log('home componentWillMount');
  }
  render() {
    return (
      <div>Home</div>
    )
  }
}

Profile组件。

class Profile extends PureComponent {
  componentWillMount() {
    console.log('Profile componentWillMount');
  }
  render() {
    return (
      <div>Profile</div>
    )
  }
}

我们可以看到使用这个生命周期函数报了警告。这是因为这个生命周期函数不推荐使用了,按照它的提示,我们使用UNSAFE_componentWillMount。

image.png

  UNSAFE_componentWillMount() {
    console.log('home componentWillMount');
  }

同样地,Profile组件也进行替换。

此时,我们看到UNSAFE_componentWillMount在严格模式下下是不被推荐的,提示在Home组件中进行更新。

image.png

2. 使用过时的ref API

react基础(十一)这篇文章中,我们介绍了ref的使用,其中ref中传入字符串是不推荐的。那么在严格模式下传入字符串会怎么样呢?

<div ref="title">Home</div>
<div ref="title">Profile</div>

我们可以看到警告提示在严格模式下应该尽量避免使用字符串的ref,而非严格模式下是可以使用的。 image.png

3. 使用废弃的findDOMNode方法

在之前的React API中,可以通过findDOMNode来获取DOM,不过已经不推荐使用了。

4. 检查意外的副作用

进行严格模式检查Home组件的constructor会被调用两次。这是严格模式下故意进行的操作,让你来看看在这里写的一些逻辑被调用多次时,是否会产生一些副作用。在生产环境中,是不会被调用两次的。

我们来测试下。

  constructor(props) {
    super(props)
    console.log('home constructor');
  }

我们可以看到在Home组件中constructor被调用了两次,而Profile组件的constructor被调用了一次。

image.png

5. 检查过时的context API

早期的Context是通过static属性声明Context对象属性,通过getChildContext返回Context对象等方式来使用Context的,目前这种方式已经不推荐使用。