React hooks的闭包陷阱

react hooks 陷阱

  • hooks必须放在函数顶层, 不能在条件分支和方法内

1、useState陷阱

异步陷阱

function Index() {
  const [count, setCount] = useState(0)
  
  function add(){
  	setCount( count + 1 )
  	console.log(count);  // 0
  }
  return (
  	<div>
		<span>{count}</span>
		<button @click=()=>{ add() }> + </button>
 	</div>
  )
}

点击一次按钮,发现页面是更新了,但是console还是上一次的值

【解决方法】:

  • 所以我们不能修改后,把值去拿去其他操作 (应该拿 count+1)
  • 可以通过 promise 来 .then 获取 最新 👇
function Index() {
  const [count, setCount] = useState(0)
  
  function add(){
    new Promise((resolve) => {
      setCount((count) => {
        resolve(count + 1)
        return count + 1
      })
    }).then((res) => {
      // 下一步操作
      console.log(res)
    })
  }
  return (
  	<div>
		<span>{count}</span>
		<button onClick={add}> ++ </button>
 	</div>
  )
}

合并陷阱,只执行最后一个

function Index() {
  const [count, setCount] = useState(0)
  
  function add(){
  	setCount( count + 1 )
  	setCount( count + 2 )
  	setCount( count + 3 ) // 只执行这个
  }
  return (
  	<div>
		<span>{count}</span>
		<button onClick={add}> ++ </button>
 	</div>
  )
}

此时 只执行了 最后一个 setCount , 导致数据部分逻辑未执行
如果出现这种 判断条件多次 操作 useState 怎么解决 👇

function add(){
	let num = count;
	if(...) { num += 1; }
	if(...) { num += 2; }
	if(...) { num += 3; }
	setCount( num );
 }

那就不写多个setCount

再看个例子:

function Index() {
  const [count, setCount] = useState(100);
  
  function add(){
    // 合并
  	setCount( count + 1 )
  	setCount( count + 1 )
    console.log(1, count) // 100
    
    // 传入函数,不会合并
  	setCount( count => count + 1 )
    setCount( count => count + 3 )
    console.log(2, count) // 100
    // 点击一次后 count显示为105
  }
  return (
  	<div>
      <span>{count}</span> 
      <button onClick={() => add() } > + </button>
    </div>
  )
}

useState初始化值,只初始化一次
// render: 初始化state
// re-lender 只恢复初始化的state值,不会再重新设置新的值,只能用setXxx修改
在这里插入图片描述

2、useEffect 陷阱

过期闭包

function Index() {
  const [count, setCount] = useState(0)
  
  useEffect(()=>{
    setInterval(() => {   console.log(`Count: ${count}`)  }, 1000)
  }, [])
 
  return (
  	<div>
		<span>{count}</span>
		<button onClick=()=>{ setCount(count+1) }> + </button>
 	</div>
  )
}

默认就会运行 每隔1s打印0
点击按钮,count有更新,但是console一直是0
说明此时的 useEffect 中的 count ,还是取的 过期的值

react特点,每次更新都会重新执行这个函数,每次就+1, 是另外一个函数了,不是原来这个函数
但是setInterval的count永远是第一个函数里面的,形成了闭包

依赖为【】时,re-render不会重新执行useEffect
没有依赖时,re-render会重新执行useEffect

【解决方法】:
需要,添加依赖项 count ,
并且每次更新,添加计时器,结束改变计时器

function Index() {
  const [count, setCount] = useState(0);
  
  useEffect(()=>{
   	const time = setInterval(() => {   console.log(`Count: ${count}`)  }, 1000)
  	return () => { clearInterval(time) } // 关键  清除上一次的setInterval 
  }, [count])
 
  return (
  	<div>
		<span>{count}</span>
		<button @click=()=>{ setCount(count+1) }> + </button>
 	</div>
  )
}

useEffect依赖引用类型,会出现死循环

3、useCallback 陷阱

useCallback 本来拿来优化性能,当依赖变化不用重新注册该函数
使用不当 也会出现一定的问题

获取父组件的值,不是最新

function Parent() {
  let [count, setCount] = useState(0);
  return (
    <div>
      <button onClick={() => setCount(count+1)}> +1 </button>
      // 子组件
      <Child count={count} />
    </div>
  )
}

function Child(props){
  let log = useCallback(() => {  console.log(props.count)  }, [])
  return (
    <div>
      count: {props.count}
      <button onClick={() => log()}> 打印 </button>
    </div>
  )
}

此时我们在 父组件点击 增加按钮
子组件的 count 发生改变 ,我们在点击打印按钮,发现count 一直是0
说明useCallback 依赖为【】数组,取到count 已经过期了

【解决方法】
方法1 :等于没有优化 (依赖更新,useCallback重写一次)

  let log = useCallback(() => {  console.log(props.count)  }, [props.count])

方法2 :将获取 count 的方法 创建到父组件,子组件调用父组件方法

function Parent() {
  let [count, setCount] = useState(0);
  let log = useCallback(() => {  console.log(count)  }, [])
  return (
    <div>
      <button onClick={() => setCount(count+1)}> +1 </button>
      // 子组件
      <Child count={count} log={log} />
    </div>
  )
}

function Child(props){
  return (
    <div>
      count: {props.count}
      <button onClick={() => props.log()}> 打印 </button>
    </div>
  )
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老电影故事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值