React Hooks 知识点

173 阅读3分钟

1、简介

image.png

image.png

image.png

image.png

image.png

image.png

2、state hooks

image.png

import React, {useState} from "react";

function ClickHooks() {
    // useState就是一个'钩',最基本的一个 Hook
    const [name, setName] = useState('Lisa')
    // 等价于
    // const arr = useState(0)
    // const name = arr[0]
    // const useName = arr[1]

    const handleClick = () => {
        setName(name + '2020')
    }

    return <div>
        <h1>{name}</h1>
        <button onClick={handleClick}>点击</button>
    </div>
}

export default ClickHooks

总结

image.png

hooks 命名规范

image.png

3、Effect hooks

image.png

image.png

image.png

import React, {useState, useEffect} from "react";

function LifeCircles() {
    const [name, setName] = useState('Lisa')
    const [count, setCount] = useState(0)

    const handleClick = () => {
        setName(name + '2020')
        setCount(count + 1)
    }
    // 模拟class 组件的 didMount 和 didUpdate 生命周期
    // useEffect(() => {
    //     console.log('发送了一个ajax请求')
    // }) // 没有第二个参数

    // 只模拟 DidMount 生命周期
    useEffect(() => {
        console.log('加载了')
    }, []) // 第二个参数为 [],不依赖任何 state

    // 只模拟 DidUpdate 生命周期
    useEffect(() => {
        console.log('更新了')
    }, [count, name]) // 第二个参数为依赖的 state

    // 模拟 class 组件的 willUnMount
    useEffect(() => {
        const timer = window.setInterval(() => {
            console.log('11111111')
        }, 200000)

        // 返回一个函数,模拟class 组件的 willUnMount
        return () => {
            window.clearInterval(timer)
        }
    }, []) // 第二个参数为依赖的 state

    return <div>
        <h1>{name}</h1>
        <h1>{count}</h1>
        <button onClick={handleClick}>点击</button>
    </div>
}

export default LifeCircles

模拟willUnMount,但不完全相等

image.png

image.png

image.png

4、其他hooks

useRef

import React, {useEffect, useRef} from "react";

function LifeCircles() {
    const btnRef = useRef(null)

    useEffect(() => {
        console.log(btnRef.current) // Dom 节点
    })
    return <div>
        <button ref={btnRef}>点击</button>
    </div>
}

export default LifeCircles

useContext

import React, {useContext} from "react";

const themes = {
    light: {
        background: '#ccc'
    },
    dark: {
        background: '#000',
        color: '#ccc'
    }
}
// 创建context
const ThemeContext = React.createContext(themes.light)

function ThemeButton() {
    const theme = useContext(ThemeContext)
    return <button style={{background: theme.background, color: theme.color}}>hello world</button>
}

function ToolBar() {
    return <ThemeButton></ThemeButton>
}

function UseContextDemo() {
    return <div>
        <ThemeContext.Provider value={themes.dark}>
            <ToolBar></ToolBar>
        </ThemeContext.Provider>
    </div>
}

export default UseContextDemo

useReducer

import React, {useReducer} from "react";

const initialState = {count: 0}
const reducer = (state, action) => {
    switch (action.type) {
        case 'increment':
            return {count: state.count + 1}
        case 'decrement':
            return {count: state.count - 1}
        default:
            return state
    }
}

function UseReducerDemo() {
    const [state, dispatch] = useReducer(reducer, initialState)
    return <div>
        count: {state.count}
        <button onClick={() => dispatch({type: 'increment'})}>+</button>
        <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </div>
}

export default UseReducerDemo

image.png

useMemo

import React, {useState, memo, useMemo} from "react";

// 子组件
// function Person({info}) {
//     console.log('child mount')
//     return <div>
//         <h1>{info.name}</h1>
//         <h1>{info.age}</h1>
//     </div>
// }

// 类似 class PureComponent,对 props进行浅比较
const Person = memo(({info}) => {
    console.log('child mount')
    return <div>
        <h1>{info.name}</h1>
        <h1>{info.age}</h1>
    </div>
})

function UseMemoDemo() {
    console.log('parent mount')

    const [count, setCount] = useState(0)
    const [name, setName] = useState('Lisa')
    const [age, setAge] = useState(20)

    // const person = {name, age}
    // 用 useMemo 缓存数据,有依赖
    const person = useMemo(() => {
        return {name, age}
    }, [name, age])

    return <div>
        <h2>{count}</h2>
        <button onClick={() => setCount(count + 1)}>Add</button>
        <Person info={person}></Person>
    </div>
}

export default UseMemoDemo

image.png

useCallback

import React, {useState, memo, useMemo, useCallback} from "react";

// 类似 class PureComponent,对 props进行浅比较
const Person = memo(({info, handleChange}) => {
    console.log('child mount')
    return <div>
        <input type="text" onChange={handleChange}/>
        <h1>{info.name}</h1>
        <h1>{info.age}</h1>
    </div>
})

function UseCallBackDemo() {
    console.log('parent mount')

    const [count, setCount] = useState(0)
    const [name, setName] = useState('Lisa')
    const [age, setAge] = useState(20)

    // const person = {name, age}
    // 用 useMemo 缓存数据,有依赖
    const person = useMemo(() => {
        return {name, age}
    }, [name, age])

    // const handleChange = (e) => {
    //     console.log(e.target.value)
    // }
    const handleChange = useCallback((e) => {
        console.log(e.target.value)
    }, [])

    return <div>
        <h2>{count}</h2>
        <button onClick={() => setCount(count + 1)}>Add</button>
        <Person info={person} handleChange={handleChange}></Person>
    </div>
}

export default UseCallBackDemo

image.png

5、自定义hooks

image.png

image.png

image.png

image.png

image.png

6、 规范和注意事项

image.png

image.png

错误示范:

image.png

image.png

7、hooks 依赖于调用顺序

image.png

8、class 组件逻辑复用有哪些问题 ?

image.png

image.png

image.png

9、Hooks 组件逻辑复用有哪些好处 ?

// react hooks 实现逻辑复用

import React, {useEffect, useState} from "react";

function useMousePosition() {
    const [x, setX] = useState(0)
    const [y, setY] = useState(0)


    useEffect(() => {
        function getMousePosition(event) {
            setX(event.clientX)
            setY(event.clientY)
        }

        document.body.addEventListener('mousemove', getMousePosition)
        return () => document.body.removeEventListener('mousemove', getMousePosition)
    }, [])

    return [x, y]
}

export default useMousePosition
import useMousePosition from './view/useMousePosition'

function App() {
    const [x, y] = useMousePosition()
    return <div className="App">

        <div style={{width: '100%', height: '200px', backgroundColor: '#ccc'}}>
            <h1>鼠标位置:{x} - {y}</h1>
        </div>
        
    </div>
}

export default App;

image.png image.png

10、hooks 使用中的几个注意事项 ?

image.png

代码演示坑一:

image.png

代码演示坑二:

image.png

image.png

image.png

image.png

依赖为 [] 的时候,re-render 不会重新执行 effect 函数; 没有依赖,re-render 会重新执行 effect 函数

代码演示坑三:

image.png

如果useEffect中的依赖为数组或者是对象的话,可能会出现死循环,原因是底层使用 objece.is()进行比较依赖是否变化 image.png

针对坑二,可以使用 useRef解决

image.png

11、面试题

1、为什么会有 react hooks,他解决了哪些问题 ?

image.png

image.png

2、React Hooks 如何模拟组件生命周期 ?

image.png

image.png

3、如何自定义 Hook ?

image.png

4、React Hooks 性能优化 ?

image.png

5、使用 React Hooks 遇到哪些坑 ?

image.png

6、Hooks 相比 HOC 和 Render Props 有哪些优点 ?

image.png