在 React 中,Hooks 是一种在函数组件中使用状态和其他 React 特性(如生命周期方法)的新方式。它们在 React 16.8 中被引入,并且极大简化了组件的状态管理和副作用处理。
常见的 React Hook
- useState
- useEffect
- useContext
- useReducer
- useRef
- useMemo
- useCallback
- useLayoutEffect
- useImperativeHandle
1. useState
useState
是最基本的 Hook,用于在函数组件中添加状态。
示例:
javascript">import React, { useState } from 'react';function Counter() {const [count, setCount] = useState(0); // count 为状态变量,setCount 为更新该状态的函数return (<div><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);
}export default Counter;
useState(0)
中的0
是初始状态值。setCount
是更新状态的方法。
2. useEffect
useEffect
用于处理副作用,类似于类组件中的生命周期方法(如 componentDidMount
, componentDidUpdate
, componentWillUnmount
)。
示例:
javascript">import React, { useState, useEffect } from 'react';function Example() {const [count, setCount] = useState(0);// 组件挂载时运行useEffect(() => {console.log('Component mounted or count changed!');document.title = `You clicked ${count} times`;// 返回的函数是清理函数,在组件卸载时调用return () => {console.log('Cleanup for count change!');};}, [count]); // 依赖数组,表示只有当 count 改变时才执行副作用return (<div><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);
}export default Example;
useEffect
第一个参数是一个副作用函数,第二个参数是依赖数组。- 如果依赖数组为空(
[]
),副作用函数只会在组件挂载和卸载时执行。
3. useContext
useContext
用于在函数组件中访问 React 上下文(Context)中的值。
示例:
javascript">import React, { useContext } from 'react';// 创建上下文
const MyContext = React.createContext('default value');function Example() {const value = useContext(MyContext); // 访问上下文值return <div>{value}</div>;
}function App() {return (<MyContext.Provider value="Hello, world!"><Example /></MyContext.Provider>);
}export default App;
useContext(MyContext)
用于获取MyContext
提供的值。
4. useReducer
useReducer
是一种更复杂的状态管理方式,类似于 useState
,但是它适用于处理复杂的状态逻辑,尤其是有多个子状态的情况。它通常用于处理复杂的状态更新逻辑,或者在 React 应用中使用类似 Redux 的模式。
示例:
javascript">import React, { useReducer } from 'react';// 定义 reducer 函数
function counterReducer(state, action) {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };default:return state;}
}function Counter() {const [state, dispatch] = useReducer(counterReducer, { count: 0 });return (<div><p>Count: {state.count}</p><button onClick={() => dispatch({ type: 'increment' })}>Increment</button><button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button></div>);
}export default Counter;
useReducer
接受两个参数:reducer 函数和初始状态。dispatch
是用于触发reducer
更新状态的函数。
5. useRef
useRef
用于获取 DOM 元素的引用或者持久化值。在重新渲染时,useRef
不会导致组件重新渲染,它常用于访问 DOM 元素或保持跨渲染周期不变的值。
示例:
javascript">import React, { useRef } from 'react';function FocusInput() {const inputRef = useRef();const focusInput = () => {inputRef.current.focus(); // 让 input 元素获取焦点};return (<div><input ref={inputRef} type="text" /><button onClick={focusInput}>Focus the input</button></div>);
}export default FocusInput;
useRef
返回一个对象,该对象的current
属性指向 DOM 元素或任何可变值。
6. useMemo
useMemo
用于缓存计算结果,以避免不必要的重新计算。它只会在依赖项发生变化时重新计算结果。
示例:
javascript">import React, { useMemo } from 'react';function Example({ num }) {const expensiveComputation = (num) => {console.log('Computing...');return num * 2;};const computedValue = useMemo(() => expensiveComputation(num), [num]);return <div>{computedValue}</div>;
}export default Example;
useMemo
通过依赖数组来优化性能,避免在每次渲染时执行高昂的计算。
7. useCallback
useCallback
用于返回一个 memoized 版本的回调函数,只有当依赖项发生变化时才会更新。它常用于将回调函数传递给子组件,以避免不必要的重新渲染。
示例:
javascript">import React, { useCallback, useState } from 'react';function Button({ onClick }) {return <button onClick={onClick}>Click me</button>;
}function Parent() {const [count, setCount] = useState(0);const increment = useCallback(() => setCount(count + 1), [count]);return (<div><Button onClick={increment} /><p>Count: {count}</p></div>);
}export default Parent;
useCallback
确保increment
函数只有在count
改变时才会重新创建。
8. useLayoutEffect
useLayoutEffect
和 useEffect
类似,不同之处在于它会在 DOM 更新之前同步执行。通常用于读取布局和同步触发副作用。
示例:
javascript">import React, { useLayoutEffect, useState } from 'react';function LayoutExample() {const [width, setWidth] = useState(0);useLayoutEffect(() => {setWidth(window.innerWidth);}, []);return <div>Window width: {width}</div>;
}export default LayoutExample;
useLayoutEffect
在 DOM 更新后立即执行,通常用于在页面渲染之前进行一些测量。
9. useImperativeHandle
useImperativeHandle
用于自定义暴露给父组件的实例值。通常与 forwardRef
配合使用,用于在函数组件中暴露 ref。
示例:
javascript">import React, { useImperativeHandle, forwardRef, useRef } from 'react';const MyComponent = forwardRef((props, ref) => {const localRef = useRef();useImperativeHandle(ref, () => ({focus: () => {localRef.current.focus();}}));return <input ref={localRef} />;
});function Parent() {const inputRef = useRef();const focusInput = () => {inputRef.current.focus(); // 使用子组件暴露的 focus 方法};return (<div><MyComponent ref={inputRef} /><button onClick={focusInput}>Focus the input</button></div>);
}export default Parent;
useImperativeHandle
用于控制外部组件访问的实例值。
总结
React 的 Hook 提供了许多强大的功能,使函数组件能够处理状态、生命周期、引用等。掌握这些常用的 Hook,能够帮助你在开发中写出更加简洁、可维护的代码。