Skip to content

useCallback(fn, dependencies)

useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。

TIP

当传递一个函数给子组件的时候,父组件如果刷新了,此时函数会重新变化内存地址,导致子组件(已经使用 memo 进行缓存)也会刷新,因此需配合 useCallback 将传递的函数进行缓存,子组件才不会刷新

javascript
// 在 React 内部的简化实现
function useCallback(fn, dependencies) {
    return useMemo(() => fn, dependencies)
}

useContext(SomeContext)

javascript
import { createContext, useContext } from 'react'

const ThemeContext = createContext(null)

export default function MyApp() {
    return (
        <ThemeContext.Provider value="dark">
            <Form />
        </ThemeContext.Provider>
    )
}

function Form() {
    return (
        <Panel title="Welcome">
            <Button>Sign up</Button>
            <Button>Log in</Button>
        </Panel>
    )
}

function Panel({ title, children }) {
    const theme = useContext(ThemeContext)
    const className = `panel-${theme}`
    return (
        <section className={className}>
            <h1>{title}</h1>
            {children}
        </section>
    )
}

function Button({ children }) {
    const theme = useContext(ThemeContext)
    const className = `button-${theme}`
    return <button className={className}>{children}</button>
}

useEffect(setup, dependencies?)

useEffect 是一个 React Hook,它允许你 将组件与外部系统同步。( 可以用于接口请求,避免组件重新渲染导致的重新请求,返回值用于清除副作用)

javascript
import { useEffect } from 'react'
import { createConnection } from './chat.js'

function ChatRoom({ roomId }) {
    const [serverUrl, setServerUrl] = useState('https://localhost:1234')

    useEffect(() => {
        const connection = createConnection(serverUrl, roomId)
        connection.connect()
        return () => {
            connection.disconnect()
        }
    }, [serverUrl, roomId])
    // ...
}

useId()

可以生成传递给无障碍属性的唯一 ID。

javascript
// 举例来说,假设你不想暴露出整个 <input> DOM 节点,但你想要它其中两个方法:focus 和 scrollIntoView。为此,用单独额外的 ref 来指向真实的浏览器 DOM。然后使用 useImperativeHandle 来暴露一个句柄,它只返回你想要父组件去调用的方法:
import { forwardRef, useImperativeHandle, useRef } from 'react'

const MyInput = forwardRef((props, ref) => {
    const inputRef = useRef(null)

    useImperativeHandle(ref, () => {
        return {
            focus() {
                inputRef.current.focus()
            },
            scrollIntoView() {
                inputRef.current.scrollIntoView()
            },
        }
    }, [])

    return (
        <input
            {...props}
            ref={inputRef}
        />
    )
})

forwardRef(render)

允许组件使用 ref 将 DOM 节点暴露给父组件。(就是将子组件的 dom 暴露出来,父组件可以使用 useRef 来进行关联,从而操作子组件的 dom)

javascript
// 子组件
import { forwardRef } from 'react'

const MyInput = forwardRef((props, ref) => {
    const { label, ...otherProps } = props
    return (
        <label>
            {label}
            <input
                {...otherProps}
                ref={ref}
            />
        </label>
    )
})

// 父组件
function Form() {
    const ref = useRef(null)

    function handleClick() {
        ref.current.focus()
    }

    return (
        <form>
            <MyInput
                label="Enter your name:"
                ref={ref}
            />
            <button
                type="button"
                onClick={handleClick}
            >
                编辑
            </button>
        </form>
    )
}

memo(Component, arePropsEqual?)

允许你的组件在 props 没有改变的情况下跳过重新渲染

javascript
import { memo } from 'react'

const Greeting = memo(({ name }) => {
    return (
        <h1>
            Hello,
            {name}!
        </h1>
    )
})

useLayoutEffect(setup, dependencies?)

useLayoutEffect 是 useEffect 的一个版本,在浏览器重新绘制屏幕之前触发。

javascript
// 调用 useLayoutEffect 在浏览器重新绘制屏幕之前进行布局测量:
import { useLayoutEffect, useRef, useState } from 'react'

function Tooltip() {
    const ref = useRef(null)
    const [tooltipHeight, setTooltipHeight] = useState(0)

    useLayoutEffect(() => {
        const { height } = ref.current.getBoundingClientRect()
        setTooltipHeight(height)
    }, [])
}

useReducer(reducer, initialArg, init?)

跟 useState 比较类似,只不过是先把 action 都定义好了,之后值的修改就通过 dispatch 进行操作

javascript
import { useReducer } from 'react'

function reducer(state, action) {
    if (action.type === 'incremented_age') {
        return {
            age: state.age + 1,
        }
    }
    throw new Error('Unknown action.')
}

export default function Counter() {
    const [state, dispatch] = useReducer(reducer, { age: 42 })

    return (
        <>
            <button
                onClick={() => {
                    dispatch({ type: 'incremented_age' })
                }}
            >
                Increment age
            </button>
            <p>
                Hello! You are
                {state.age}.
            </p>
        </>
    )
}

useRef(initialValue)

它能帮助引用一个不需要渲染的值(useRef会返回一个对象,这个对象上有 current 这个属性,对这个属性的值进行修改,不会导致组件的重新渲染;如果是对 dom 使用,则 current 会变成对这个 dom 的引用)

javascript
import { useRef } from 'react'

export default function Form() {
    const inputRef = useRef(null)

    function handleClick() {
        inputRef.current.focus()
    }

    return (
        <>
            <input ref={inputRef} />
            <button onClick={handleClick}>聚焦输入框</button>
        </>
    )
}
// or...
const intervalRef = useRef(0)
function handleStartClick() {
    const intervalId = setInterval(() => {
        // ...
    }, 1000)
    intervalRef.current = intervalId
}

useState(initialState)

javascript
import { useState } from 'react'

function MyComponent() {
    const [age, setAge] = useState(28)
    const [name, setName] = useState('Taylor')
    const [todos, setTodos] = useState(() => createTodos())
    // ...
}

DANGER

useState 是一个 Hook,因此你只能在 组件的顶层 或自己的 Hook 中调用它。你不能在循环或条件语句中调用它。如果你需要这样做,请提取一个新组件并将状态移入其中。

startTransition

用于将状态更新标记为​​低优先级(过渡更新)​​,从而避免阻塞高优先级的用户交互(如输入、点击等),提升应用的响应性和用户体验。

typescript
import { startTransition } from 'react'

// 紧急更新:输入框即时响应
setInputValue(input)

// 过渡更新:延迟处理筛选逻辑
startTransition(() => {
    setSearchResults(filterData(input))
})

// Hook 版
const [isPending, startTransition] = useTransition()

性能相关优化

https://juejin.cn/post/7491567546770276361

useActionState

根据表单动作的结果更新 state 的 Hook。

tsx
import { useActionState } from 'react'

async function increment(previousState, formData) {
    // 返回的数据可以通过 state 拿到,也可以是对象({state: 'success', message: 'ok'})等类型
    return previousState + 1
}

function StatefulForm({}) {
    const [state, formAction] = useActionState(increment, 0)
    return (
        <form action={formAction}>
            {state}
            <button type="submit">+1</button>
        </form>
    )
}