阅读 38

React Hooks总结记录

npx

  • 避免安装全局模块
  • 调用项目内部安装的模块

npm run会新建一个shell,并将node_modules加入到系统环境变量,运行完再删除

为什么要有React Hook

  • 组件很难复用状态逻辑
  • 复杂组件难以理解,尤其是生命周期函数
  • React组件一直是函数,使用Hook完全拥抱函数

useState

  • 会触发组件的render
import React, {useState} from 'react'

const LikeButton: React.FC = () => {
    const [like, setLike] = useState(0)
    const [on, setOn] = useState(true)
    return (
        <>
        <button onClick={() => {setLike( like + 1)}}>
            {like} 赞
        </button>
        <button onClick={() => {setOn(!on)}}>
            {on ? 'on' : 'off'}
        </button>
        </>
    )
}
export default LikeButton;
复制代码

useEffect

无需清除的Effect

使用useEffect使用DOm完成标题更新

image.png

    // 默认在第一次渲染之后,和每次更新之后都会执行
    useEffect(() => {
        document.title = `点击了${like}次`
    })
复制代码

需要清除的Effect

使用useEffect完成一个鼠标跟踪器 image.png

import React, {useState, useEffect} from 'react'

const MouseTracker: React.FC = () => {
    const [positions, setPositions] = useState({x: 0, y: 0})
    useEffect(() => {
        console.log('add effect', positions.x)
        const mouseTracker = (e: MouseEvent) => {
            console.log('inner')
            setPositions({x: e.x, y: e.y})
        }
        document.addEventListener('click', mouseTracker)
        return () => {
            console.log('remove effect', positions.x)
            document.removeEventListener('click', mouseTracker)
        }
    })
    console.log('before render', positions.x)
    return (
        <p>X: {positions.x}, Y: {positions.y}</p>
    )
}

export default MouseTracker
复制代码

image.png add effect总是在before render之后执行

控制useEffect的执行

 useEffect(() => {
        console.log('add effect', positions.x)
        const mouseTracker = (e: MouseEvent) => {
            console.log('inner')
            setPositions({x: e.x, y: e.y})
        }
        document.addEventListener('click', mouseTracker)
        return () => {
            console.log('remove effect', positions.x)
            document.removeEventListener('click', mouseTracker)
        }
    }, [])
复制代码

这里实际就是componentDidMount和componentWillUnmount

自定义Hook

  • 将组件逻辑提取到可重用的函数中
  • 代码:使用自定义Hook抽象鼠标跟踪器
import React, {useState, useEffect} from 'react'

const useMousePosition = () => {
    const [positions, setPositions] = useState({x: 0, y: 0})
    useEffect(() => {
        console.log('add effect', positions.x)
        const mouseTracker = (e: MouseEvent) => {
            console.log('inner')
            setPositions({x: e.x, y: e.y})
        }
        document.addEventListener('mousemove', mouseTracker)
        return () => {
            console.log('remove effect', positions.x)
            document.removeEventListener('mousemove', mouseTracker)
        }
    }, [])
    return positions;
}
export default useMousePosition
复制代码

HOC 高阶组件

  • 高阶组件就是一个函数,接受一个组件作为参数,返回一个新的组件
  • 常见实现方法:属性代理和反向继承
  • 劣势:
    • 添加不必要的界面结构,而不是单纯的逻辑
    • 难以理解
    • 想要理解,还需要去找被包裹的组件

使用自定义hook完成useURLLoader

import {useState, useEffect} from 'react'
import axios from "axios";

const useURLLoader = (url: string, deps: any[] = []) => {
    const [data, setData] = useState<any>(null);
    const [loading, setLoading] = useState(false)
    useEffect(() => {
        setLoading(true)
        axios.get(url).then(res => {
            setData(res.data)
            setLoading(false)
        })
    },deps)
    return [data, loading]
}

export default useURLLoader;
复制代码

useRef

为了解决什么问题

  • 任意一次渲染中的state值都是常量,不会随着时间改变而变,渲染输出会变,是因为组件被一次次调用,而每一次调用组件渲染时里面的state值独立于其他渲染。
  • 在任意一次渲染中props和state值是始终保持不变的,如果props和state在不同的渲染中是相互独立的,那么使用到他们任何值也是相互独立的。

作用

  • 多次渲染之间的纽带
    1. useRef在所有的render中保持唯一的引用
    2. 需要注意的是对ref值改变不会重新render
import React, {useState, useEffect, useRef} from 'react'

const LikeButton: React.FC = () => {
    const [like, setLike] = useState(0)
    // {current: 0}
    const likeRef = useRef(0)
    // 默认在第一次渲染之后,和每次更新之后都会执行
    useEffect(() => {
        document.title = `点击了${like}次`
    })
    function handleAlertClick(){
        setTimeout(() => {
            alert(`点击了 ${likeRef.current} 次`)
        },3000)
    }
    return (
        <>
            <button onClick={() => {
                setLike(like + 1)
                likeRef.current++;
            }}>
                {like} 赞
            </button>
            <button onClick={handleAlertClick}>Alert!</button>
        </>
    )
}
export default LikeButton;
复制代码
  • 模拟componentDidUpdate生命周期
import React, {useState, useEffect, useRef} from 'react'

const LikeButton: React.FC = () => {
 		const didMountRef = useRef(false)
    // 模拟componentDidUpdate
    useEffect(() => {
        if (didMountRef.current) {
            console.log('this is updated')
        } else {
            didMountRef.current = true;
        }
    })
    return (
        <>
    
        </>
    )
}
export default LikeButton;   

复制代码
  • 访问dom节点
import React, {useState, useEffect, useRef} from 'react'

const LikeButton: React.FC = () => {
    const domRef = useRef<HTMLInputElement>(null)
    // 将input focus
    useEffect(() => {
        if(domRef && domRef.current){
            domRef.current.focus()
        }
    })
  
    return (
        <>
            <input type="text" ref={domRef}/>
        </>
    )
}
export default LikeButton;
复制代码

useContext

  • 解决多层传递属性

Hook规则

  • 只在最顶层使用hook
  • 只在React函数中调用hook

其他hook

  • useReducer:简化有复杂逻辑的useState
  • useCallback
文章分类
前端
文章标签