文章目录
- 前言
- useState:管理组件状态
- useEffect:处理副作用
- useLayoutEffect:同步执行副作用
- useReducer:复杂状态逻辑管理
- useRef:访问 DOM 元素和保存变量
- forwardRef 与 useImperativeHandle:自定义 ref 的内容
- useContext:跨组件传递数据
- memo, useMemo 与 useCallback:性能优化
- 结论
前言
React Hooks 是 React 16.8 引入的一项强大功能,它们为函数组件引入了状态和其他 React 特性。以下是对 React 常用 Hooks 的详细介绍和使用指南。
useState:管理组件状态
状态是组件的核心部分。useState
是最基本的 Hook,用于在函数组件中添加状态。
import React, { useState } from 'react';function Counter() {const [count, setCount] = useState(0);return (<div><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);
}
要点:
useState
接受一个初始状态值,并返回一个状态和更新状态的函数。setState
可以接受新的状态值或一个返回新状态值的函数。
useEffect:处理副作用
useEffect
是处理副作用的 Hook,例如数据获取、订阅和手动 DOM 操作。
import React, { useState, useEffect } from 'react';function Example() {const [count, setCount] = useState(0);useEffect(() => {document.title = `You clicked ${count} times`;}, [count]); // 仅在 count 变化时执行return (<div><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);
}
要点:
useEffect
在组件渲染后执行,可以返回一个清理函数。- 依赖数组(第二个参数)决定 effect 的执行时机。
useLayoutEffect:同步执行副作用
useLayoutEffect
与 useEffect
类似,但它在所有 DOM 变更之后同步调用 effect。
import React, { useLayoutEffect, useRef } from 'react';function LayoutEffectExample() {const divRef = useRef(null);useLayoutEffect(() => {console.log(divRef.current.getBoundingClientRect());});return <div ref={divRef}>Hello World</div>;
}
要点:
useLayoutEffect
在 DOM 更新后立即同步执行。- 用于避免 DOM 更新后的闪烁,但如果计算量大可能会导致掉帧。
useReducer:复杂状态逻辑管理
useReducer
提供了一种替代 useState
的方式,用于管理更复杂的状态逻辑。
import React, { useReducer } from 'react';const initialState = { count: 0 };function reducer(state, action) {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };default:throw new Error();}
}function Counter() {const [state, dispatch] = useReducer(reducer, initialState);return (<div>Count: {state.count}<button onClick={() => dispatch({ type: 'increment' })}>+</button><button onClick={() => dispatch({ type: 'decrement' })}>-</button></div>);
}
要点:
useReducer
接受一个 reducer 函数和初始状态,返回当前状态和 dispatch 函数。- 在处理复杂状态逻辑或状态依赖的更新时非常有用。可以结合immr使用。
useRef:访问 DOM 元素和保存变量
useRef
可以保存 DOM 元素引用或其他值,改变它不会引起重新渲染。
import React, { useRef } from 'react';function TextInputWithFocusButton() {const inputEl = useRef(null);const onButtonClick = () => {inputEl.current.focus();};return (<div><input ref={inputEl} type="text" /><button onClick={onButtonClick}>Focus the input</button></div>);
}
要点:
useRef
返回一个可变的 ref 对象,其.current
属性被初始化为传入的参数(初始值)。- 用于直接访问 DOM 元素或保存可变值。
forwardRef 与 useImperativeHandle:自定义 ref 的内容
forwardRef
与 useImperativeHandle
一起使用,可以自定义父组件通过 ref 访问的子组件内容。
import React, { useImperativeHandle, forwardRef, useRef } from 'react';const FancyInput = forwardRef((props, ref) => {const inputRef = useRef();useImperativeHandle(ref, () => ({focus: () => {inputRef.current.focus();}}));return <input ref={inputRef} />;
});function Parent() {const inputRef = useRef();return (<div><FancyInput ref={inputRef} /><button onClick={() => inputRef.current.focus()}>Focus the input</button></div>);
}
要点:
forwardRef
用于将 ref 转发到子组件。useImperativeHandle
用于定义暴露给父组件的实例值。
类比:
forwardRef 与 useImperativeHandle
与vue3 中porps + expose
的使用比较
useContext:跨组件传递数据
useContext
使得我们可以在组件树中传递数据,而不需要逐层传递 props。
import React, { useContext } from 'react';const ThemeContext = React.createContext('light');function ThemeButton() {const theme = useContext(ThemeContext);return <button className={theme}>Theme Button</button>;
}function App() {return (<ThemeContext.Provider value="dark"><ThemeButton /></ThemeContext.Provider>);
}
要点:
useContext
接受一个 context 对象,并返回 context 的当前值。- 用于在组件之间共享数据而不需要逐层传递 props。
memo, useMemo 与 useCallback:性能优化
这些 hooks 用于优化组件渲染性能。
import React, { memo, useMemo, useCallback } from 'react';const ExpensiveComponent = memo(({ count }) => {return <div>{count}</div>;
});function Parent({ count }) {const memoizedValue = useMemo(() => expensiveCalculation(count), [count]);const memoizedCallback = useCallback(() => {doSomething(count);}, [count]);return (<div><ExpensiveComponent count={memoizedValue} /><button onClick={memoizedCallback}>Do something</button></div>);
}
要点:
memo
包裹的组件仅在 props 变化时重新渲染。useMemo
缓存计算结果,useCallback
缓存函数定义,避免不必要的重新创建。
结论
React Hooks 提供了强大的功能,使函数组件可以使用状态和其他 React 特性。通过理解和正确使用这些 Hooks,可以编写出更简洁、高效和可维护的 React 代码。在实际开发中,选择合适的 Hook 并结合使用是提升代码质量的关键。