【前端进阶】手撕React Hooks核心逻辑:300行代码揭示函数式组件状态管理本质

本文实现包含:
✅ 可工作的useState/useEffect
✅ 依赖收集系统
✅ 批量更新机制
✅ 闭包陷阱深度分析

一、前置概念:Hooks的运作基石

1.1 函数组件的执行特点

function MyComponent() {
  const [count, setCount] = useState(0) // 如何保持状态?
  // 每次渲染都是全新执行上下文
}

1.2 核心设计约束

  • 调用顺序稳定性:Hooks必须按固定顺序执行
  • 状态隔离性:多个组件实例互不影响
  • 副作用调度:精准控制DOM操作时机

二、极简Hooks实现(完整可运行)

2.1 全局调度器(模拟React Reconciler)

// 当前渲染的组件实例
let currentInstance = null
// 状态存储结构
const instances = new Map()

class Dispatcher {
  constructor() {
    this.hookIndex = 0
    this.effectQueue = []
  }

  // 组件挂载入口
  render(Component) {
    const instanceId = Symbol('instance')
    currentInstance = {
      id: instanceId,
      hooks: [],
      dispatcher: this
    }
    instances.set(instanceId, currentInstance)
    const vdom = Component()
    this.flushEffects()
    currentInstance = null
    this.hookIndex = 0
    return vdom
  }

  flushEffects() {
    this.effectQueue.forEach(effect => effect())
    this.effectQueue = []
  }
}

2.2 实现useState

function createUseState(initialState) {
  return () => {
    const dispatcher = currentInstance.dispatcher
    const index = dispatcher.hookIndex++
    
    const hooks = currentInstance.hooks
    if (index >= hooks.length) {
      hooks.push({
        state: typeof initialState === 'function' 
          ? initialState()
          : initialState,
        queue: []
      })
    }

    const hook = hooks[index]
    
    // 批量更新处理
    if (hook.queue.length > 0) {
      hook.state = hook.queue.reduce((state, action) => {
        return typeof action === 'function' 
          ? action(state)
          : action
      }, hook.state)
      hook.queue = []
    }

    const setState = (action) => {
      hook.queue.push(action)
      // 触发重新渲染
      dispatcher.render(currentInstance.Component)
    }

    return [hook.state, setState]
  }
}

const useState = createUseState()

2.3 实现useEffect

function useEffect(effectFn, deps) {
  const dispatcher = currentInstance.dispatcher
  const index = dispatcher.hookIndex++
  
  const hooks = currentInstance.hooks
  const hook = hooks[index] || { deps: undefined }
  
  const hasChanged = !deps || 
    !hook.deps ||
    deps.some((dep, i) => !Object.is(dep, hook.deps[i]))
  
  if (hasChanged) {
    hook.cleanup?.()
    hook.cleanup = effectFn()
    hook.deps = deps
  }
  
  dispatcher.effectQueue.push(hook.cleanup)
  dispatcher.hookIndex++
}

三、关键性能陷阱解析

3.1 闭包陷阱(Stale Closure)

function Counter() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const timer = setInterval(() => {
      console.log(count) // 永远打印初始值
    }, 1000)
    return () => clearInterval(timer)
  }, []) // 空依赖

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

解决方案:

// 正确写法:使用函数式更新
setCount(prev => prev + 1)

// 或者添加count到依赖数组
useEffect(() => {...}, [count])

3.2 无效渲染(Zombie Children)

function Parent() {
  const [show, setShow] = useState(true)
  
  return (
    <div>
      <button onClick={() => setShow(false)}>
        Remove
      </button>
      {show && <Child />}
    </div>
  )
}

function Child() {
  const [data, setData] = useState(null)
  
  useEffect(() => {
    fetchData().then(res => {
      setData(res) // 组件卸载后仍可能执行
    })
  }, [])

  return <div>{data}</div>
}

解决方案:

useEffect(() => {
  let isMounted = true
  fetchData().then(res => {
    if (isMounted) setData(res)
  })
  return () => { isMounted = false }
}, [])

3.3 依赖数组误用

const [data, setData] = useState([])

// 反模式:缺少data依赖
useEffect(() => {
  localStorage.setItem('data', JSON.stringify(data))
}, []) 

// 正确写法:
useEffect(() => {
  localStorage.setItem('data', JSON.stringify(data))
}, [data])

四、性能优化方案

4.1 批量更新优化

// 原生React行为
const onClick = () => {
  setCount(1)
  setFlag(true) 
  // 仅触发一次渲染
}

// 我们的实现需添加批处理:
function batchedUpdates(fn) {
  isBatchingUpdates = true
  fn()
  isBatchingUpdates = false
  if (!isBatchingUpdates) {
    dispatcher.render(currentInstance.Component)
  }
}

4.2 状态冻结技术

function useImmutableState(initialState) {
  const [state, setState] = useState(
    Object.freeze(initialState)
  )
  
  const updateState = (newState) => {
    setState(prev => 
      Object.freeze(
        typeof newState === 'function' 
          ? newState(prev)
          : newState
      )
    )
  }
  
  return [state, updateState]
}

五、与React官方实现的差异

特性本文实现React官方
状态存储数组+闭包链表结构
更新调度同步渲染优先级调度
生命周期简单Effect队列完整调度阶段
DevTools支持完整调试信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值