React 16.13.1触发两次render

一段很普通的代码,出发了两次render

import React, { useState, useEffect } from 'react'

const MouseTracker: React.FC = () => {
  const [ positions, setPositions ] = useState({x: 0, y: 0})
  useEffect(() => {
    console.log('add effect', positions.x)
    const updateMouse= (e: MouseEvent) => {
      console.log('inner')
      setPositions({ x: e.clientX, y: e.clientY })
    }
    document.addEventListener('click', updateMouse)
    return () => {
      console.log('remove effect', positions.x)
      document.removeEventListener('click', updateMouse)
    }
  },[])
  console.log('before render', positions.x) // 执行了两次
  return (
    <>
    <p>X: {positions.x}, Y : {positions.y}</p>
    </>
  )
}

export default MouseTracker

原因:

最近的react版本,dev模式下render使用的是strict mode,strict mode的通过两次调用constructor和render函数来更好的检测不符合预期的side effects

文档中有表明

Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

  • Class component constructor, render, and shouldComponentUpdate methods
  • Class component static getDerivedStateFromProps method
  • Function component bodies
  • State updater functions (the first argument to setState)
  • Functions passed to useState, useMemo, or useReducer

下列函数会执行两次

  • 类组件的constructor,render和shouldComponentUpdate方法
  • 类组建的静态方法getDerivedStateFromProps
  • 函数组件方法体
  • 状态更新函数(setState的第一个参数)
  • 传入useState,useMemo或useReducer的函数

在production环境下不会这样,所以不用担心