手写Hooks核心原理

手写实现useState useEffect useReducer

一. useState 钩子函数的实现原理

// import { useState } from 'react'
import ReactDOM from 'react-dom'
let state = []
let setters = []
let stateIndex = 0

function createSetter (index) {
     
  return function (newState) {
     
    state[index] = newState
    render()
  }
}

function useState (initialState) {
     
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState
  setters.push(createSetter(stateIndex))
  let value = state[stateIndex]
  let setter = setters[stateIndex]
  stateIndex ++
  return [value, setter]
}

function render () {
     
  stateIndex = 0
  ReactDOM.render(
    <App/>,
    document.getElementById('root')
  )
}

export default function App () {
     
  const [count, setCount] = useState(0)
  const [name, setName] = useState('张三')
  return <div>
    <span>{
     count}</span>
    <button onClick={
     () => setCount(count + 1)}> + 1</button>
    <span>{
     name}</span>
    <button onClick={
     () => setName('李四')}> 李四</button>
  </div>
}

二. useEffect 钩子函数的实现原理

// import { useState } from 'react'
import ReactDOM from 'react-dom'
let state = []
let setters = []
let stateIndex = 0

function createSetter (index) {
     
  return function (newState) {
     
    state[index] = newState
    render()
  }
}

function useState (initialState) {
     
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState
  setters.push(createSetter(stateIndex))
  let value = state[stateIndex]
  let setter = setters[stateIndex]
  stateIndex ++
  return [value, setter]
}

function render () {
     
  stateIndex = 0
  effectIndex = 0
  ReactDOM.render(
    <App/>,
    document.getElementById('root')
  )
}

// 上一次的依赖值
let prevDepsAry = []
let effectIndex = 0

/**
 * useEffect
 * @param {function} callback 回调函数
 * @param {Array} depsAry 依赖数组
 * @returns {function} 清理函数
 */
function useEffect (callback, depsAry) {
     
  if (Object.prototype.toString.call(callback) !== '[object Function]') throw new Error('useEffect 第一个参数必须是一个函数')
  if (typeof depsAry === 'undefined') {
     
    // 没有传递
    callback()
  } else {
     
    // 判断 depsAry 是不是数组
    if (Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error('useEffect 第二个参数必须是一个数组')
    // 获取上一次的状态值
    let prevDeps = prevDepsAry[effectIndex]
    // 将当前的依赖值和上一次的依赖值作对比,如果有变化,调用 callback
    let hasChanged = prevDeps ? !depsAry.every((dep, index) => dep === prevDeps[index]) : true
      // 判断值是否有变化
    if (hasChanged) {
     
      callback()
    }
    // 同步依赖值
    prevDepsAry[effectIndex++] = depsAry
  }
}

export default function App () {
     
  const [count, setCount] = useState(0)
  const [name, setName] = useState('张三')

  useEffect(() => {
     
    console.log('Hello')
  }, [count])

  useEffect(() => {
     
    console.log('World')
  }, [name])

  // 测试不传监听数据的情况
  useEffect(() => {
     
    console.log('xxx')
  }, [])

  return <div>
    <span>{
     count}</span>
    <button onClick={
     () => setCount(count + 1)}> + 1</button>
    <span>{
     name}</span>
    <button onClick={
     () => setName('李四')}> 李四</button>
  </div>
}

三.useReducer 钩子函数的实现原理

// import { useState } from 'react'
// import { useReducer } from 'react'
import ReactDOM from 'react-dom'
let state = []
let setters = []
let stateIndex = 0

function createSetter (index) {
     
  return function (newState) {
     
    state[index] = newState
    render()
  }
}

function useState (initialState) {
     
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState
  setters.push(createSetter(stateIndex))
  let value = state[stateIndex]
  let setter = setters[stateIndex]
  stateIndex ++
  return [value, setter]
}

function render () {
     
  stateIndex = 0
  effectIndex = 0
  ReactDOM.render(
    <App/>,
    document.getElementById('root')
  )
}

// 上一次的依赖值
let prevDepsAry = []
let effectIndex = 0

/**
 * useEffect
 * @param {function} callback 回调函数
 * @param {Array} depsAry 依赖数组
 * @returns {function} 清理函数
 */
function useEffect (callback, depsAry) {
     
  if (Object.prototype.toString.call(callback) !== '[object Function]') throw new Error('useEffect 第一个参数必须是一个函数')
  if (typeof depsAry === 'undefined') {
     
    // 没有传递
    callback()
  } else {
     
    // 判断 depsAry 是不是数组
    if (Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error('useEffect 第二个参数必须是一个数组')
    // 获取上一次的状态值
    let prevDeps = prevDepsAry[effectIndex]
    // 将当前的依赖值和上一次的依赖值作对比,如果有变化,调用 callback
    let hasChanged = prevDeps ? !depsAry.every((dep, index) => dep === prevDeps[index]) : true
      // 判断值是否有变化
    if (hasChanged) {
     
      callback()
    }
    // 同步依赖值
    prevDepsAry[effectIndex++] = depsAry
  }
}

function useReducer (reducer, initialState) {
     
  const [state, setState] = useState(initialState)
  function dispatch (action) {
     
    const newState = reducer(state, action)
    setState(newState)
  }
  return [state, dispatch]
}

export default function App () {
     

  function reducer(state, action) {
     
    switch (action.type) {
     
      case 'increment':
        return state + 1
      case 'decrement':
        return state - 1
      default:
        return state
    }
  }

  const [cnt, dispatch] = useReducer(reducer, 0)

  return <div>
    <div>
      <button onClick={
     () => dispatch({
     type: 'increment'})}> + 1</button>
      <span>{
     cnt}</span>
      <button onClick={
     () => dispatch({
     type: 'decrement'})}> - 1</button>
    </div>
  </div>
}

你可能感兴趣的:(手写实现Hooks核心API)