浅谈ReactHook 和部分hook 原生实现

187 阅读3分钟

ReactHook是什么?

对函数型组件进行增强,让函数型组件可以储存状态,可以拥有处理副作用的能力。让开发者在不使用类组件的情况下,实现相同的功能。相比之下ReactHook比类组件使用更加容易上手,逻辑更加容易复用,也避免很多this指向不明确的问题。

扩展: (函数副作用的含义: 在I/O模型中,我们希望在在I到O之间只有计算,如果中间包含且不仅包含触发了其他I/O、与此次I -> O计算并不相关的任何事情,都称为副作用。典型例子:1.与外界交互的;2.调用I/O的;3.从函数范围之外检索值;4.磁盘检索;5.抛出异常)

React Hooks 的钩子函数

useState => 原本函数型组件在使用完后,内部的变量就会被释放掉。而这个钩子通过闭包可以让函数型组件可以保存状态,保存内部的变量。注意:1、设置状态值方法的参数,可以是一个值也可以是一个函数。2、useState 设置状态值方 是异步的。3、初始值可以是任意数据类型的值,也可以是个函数返回值(动态设置初始值)。

useReducer => 另外一种让函数保存状态的形式。适用于state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等

function reducer(state,action){
    swich(action.type){
        case "decrement"
            return state - 1
    }
}
const [count, dispatch] = useReducer(reducer,0)

<button onClick = {() => {dispatch({type:"decrement"})}> -1 <button>

useContext => 简化跨组件层级获取数据代码

useEffect => 拥有处理副作用的能力,类似组件生命周期函数

执行时机:

image.png

相比类组件生命周期的优点:可以多次调用,区分不同业务逻辑代码。

结合异步函数:useEffect里面的函数不能直接是异步函数,因为useEffect 默认返回清理函数的函数,而异步函数会返回Promise对象。故使用函数自执行的方法来实现包装这个异步函数。

useEffect (()=>{(async function() { await xxx  })()},[] )

useMemo => 类似Vue 的计算属性,检测某个值的变化,根据变化值,计算新值。但是useMemo 会缓存计算结果,如果检测值没有变化,即使组件从新渲染,也不会重新计算,此方法有助于避免每次渲染进行昂贵的计算。

memo => 性能优化,如果本组件的数据没有变化,阻止组件更新。类似类组件的shouldComponentUpdate 和 pureComponent

useCallback => 性能优化,缓存函数,使组件重新渲染时得到相同的函数实例。尤其是对子组件传递函数方法的时候,使用该方法。

useRef => 1.获取Dom元素;2.保存数据,即使组件从新渲染,保存的数据还在,保存的数据被更改不会触发组件重新渲染。能够跨生命周期保存数据。

自定义Hooks => 标准的封装和共享逻辑的函数,以use开头。

部分钩子函数的原理实现

useState, useEffect

import ReactDOM from "react-dom"
let state = []
let setters = []
let stateIndex = 0
let effectIndex = 0
function render () {
    stateIndex = 0
    effectIndex = 0
    ReactDOM.render(<App/>,document.getElementById("root"))
}
function createSetter(index){
    return function setState(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]
}
let preDepsAry = []
function useEffect (callback,depsAry){
    // 判断callback 是否是函数
    if(Object.prototype.toString.call(callback) !== '[object Function]') throw new Error("useEffect 函数的第一个参数必须是函数")
    // 判断depsAry有没有传递
    if(typeof depsAry === "undefined"){
        callback()
    }else {
        // 判断是不是数组
        if(Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error ("useEffect 函数第二个参数必须是数组")
        let prevDeps = preDepsAry[effectIndex]
        // 判断是否有变化
        let hasChange =  prevDeps ?  depsAry.every((depsItem,index) => depsItem === prevDeps[index]) === false  : true
       if(hasChange){
        callback()
       }
       //同步依赖值
       preDepsAry[effectIndex] = depsAry
       effectIndex++
    }


}
function App() {
  const [count,setCount] = useState(0)
  const [name,setName] = useState('张三')
  useEffect (() => {
    console.log(22);
  },[count])
  useEffect (() => {
    console.log(44);
  },[name])
    return (
        <div className="App">
            {count}
            {name}
            <button onClick = {( ) => {setCount(count + 1)}}>setCount</button>
            <button onClick = { ( ) => {setName("李四")}}>setName</button>
            {/* <Colors> 
                <ShowArea></ShowArea>
                <Buttons></Buttons>
            </Colors> */}
            
        </div>
    );
}

export default App;