阅读代码题

面试给一段代码,找出 Bug 或说出输出

1 闭包陷阱(setInterval + useState)

找出这段代码的问题
function Counter() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const timer = setInterval(() => {
      setCount(count + 1)  // ❌ 闭包陷阱:count 永远是 0
    }, 1000)
    return () => clearInterval(timer)
  }, [])

  const handleClick = () => {
    setCount(count + 1)    // ❌ 同样的问题
  }

  return <button onClick={handleClick}>{count}</button>
}

问题分析

  • 闭包陷阱useEffect 的依赖数组是 [],回调函数在初次渲染时定义,捕获的 count 永远是 0
  • setInterval 每秒执行 setCount(0 + 1),count 永远停在 1

修复方法:使用函数式更新

useEffect(() => {
  const timer = setInterval(() => {
    setCount(prev => prev + 1)   // ✅ 函数式更新,不依赖闭包
  }, 1000)
  return () => clearInterval(timer)
}, [])

const handleClick = () => {
  setCount(prev => prev + 1)     // ✅
}

2 React 综合找 Bug

一段代码里有 4 个问题,你能找到几个?
function Component(props) {
  const [count, setCount] = useState(() => {
    return props.initialCount ?? 0 // ❌ 问题1:初始化函数只执行一次,props变了也不会更新
  })

  useEffect(() => {  // ❌ 问题2:没有依赖数组,每次渲染都执行
    if (props.shouldAddListener) {
      setTimeout(() => {
        window.addEventListener('click', () => { // ❌ 问题3:没有 cleanup 清除监听
          setCount(count + 1) // ❌ 问题4:闭包陷阱,count 永远是旧值
        })
      }, 0)
    }
  });

  const clickFun = () => setCount(count + 1) // ❌ 同样闭包问题

  return <button onClick={clickFun}>{count}</button>
}

4 个问题

  • 问题 1 — useState 初始化不响应 props 变化useState(() => props.initialCount ?? 0) 只在首次渲染执行。props 变化后 count 不会更新 → 需要加 useEffect 同步
  • 问题 2 — useEffect 没有依赖数组useEffect(() => { ... }) 没有第二个参数 → 每次渲染都会执行,疯狂绑定事件监听
  • 问题 3 — 事件监听没有清除addEventListener 没有 return cleanup → 内存泄漏
  • 问题 4 — 闭包陷阱setCount(count + 1) 在事件监听回调中捕获了旧的 count → 应该用 setCount(prev => prev + 1)

修复版

function Component(props) {
  const [count, setCount] = useState(() => props.initialCount ?? 0)

  useEffect(() => {
    setCount(props.initialCount ?? 0)
  }, [props.initialCount])

  useEffect(() => {
    if (!props.shouldAddListener) return
    const handler = () => setCount(prev => prev + 1)
    window.addEventListener('click', handler)
    return () => window.removeEventListener('click', handler)
  }, [props.shouldAddListener])

  return <button onClick={() => setCount(prev => prev + 1)}>{count}</button>
}