Skip to main content

解决React执行两次render的问题

原因

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

下列函数会执行两次

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

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

【BUG:本地数据与显示数据不一致】

设置两次数据,第一次从 undefined 到 [ ],第二次再从 [ ][数据]

【解决方法一】

  • 使用计数监听配合 useEffec防止设置两次数据
  • 只有>1执行代码,第一次设置无作用,只保留第二次设置
import React, { useRef }  from 'react'
import { useEffect, useState } from 'react';

const A = () => {
const [data,setData] = useState(0)
const count = useRef(0) //存放自定义属性,默认值为0

const add = () => {
setData(data + 1)
//setData会触发两次渲染,两次data数据并没有合并,只把首次数据(0)传给了localStorage,所以本地数据与实时数据不一致
// window.localStorage.setItem('value',data)
}

useEffect(() => { //出生/更新:每次计算渲染
count.current += 1
})
useEffect(() => { //只有>1执行代码
if (count.current > 1) {
window.localStorage.setItem('value',data)
}
}, [data])

return (
<>
{data}
<button onClick={add}>+1</button>
</>
)
}

优化计数代码

封装自定义Hook

  • 新建src/hooks目录

    • 新建useUpdate文件
  • 封装useUpdate函数

    • 引入React/useRef/useEffect

      import React, { useRef,useEffect }  from 'react'
    • 声明useUpdate函数

      const useUpdate = (fn,deps) => {
      const count = useRef(0)
      useEffect(() => {
      count.current += 1
      })
      useEffect(() => {
      if (count.current > 1) {
      fn()
      }
      }, deps)
      }

      export {useUpdate}

重构组件

  • 引入useUpdate
    import { useUpdate } from '../hooks/useUpdate';
  • 使用useUpdate函数
     useUpdate(() => {
    window.localStorage.setItem('value',data)
    }, [data])
  • 组件完整代码
import React from 'react'
import {useState } from 'react';
import { useUpdate } from '../hooks/useUpdate';

const A = () => {
const [data, setData] = useState(0)

const add = () => {
setData(data + 1)
}

useUpdate(() => {
window.localStorage.setItem('value',data)
}, [data])


return (
<>
{data}
<button onClick={add}>+1</button>
</>
)
}

export default A

【解决方法二】

setTimeout定时器

  useEffect(() => {
const timeout = setTimeout(() =>
verifyIsLogin(), 300);
return () => clearTimeout(timeout);
async function verifyIsLogin() {

};
// verifyIsLogin();
}, []);

链接

react hooks useEffect 执行两次解决方案 javascript - useEffect 调用了两次 解决useEffect重复调用问题 React + Hooks中组件重复渲染的问题