React 的 Hooks 为函数组件引入了之前只能在类组件中使用的功能和生命周期特性。以下是一些常用的 Hooks:
1. useState
:
允许函数组件有内部状态。返回一个状态变量和一个设置该状态的函数。
const [count, setCount] = useState(0);
2. useEffect
:
用于执行副作用操作,如数据获取、手动更改 DOM 和设置订阅等。它也可以模拟类组件中的生命周期方法。
useEffect(() => {document.title = `Count: ${count}`;
}, [count]); // 当 count 改变时,这个副作用将重新运行
3. useContext
:
允许你使用 context 无需显式地使用 Context.Consumer
。
useContext
是 React 提供的一个 Hook,它允许你在函数组件中访问当前的 context 值。为了使用 useContext
,你首先需要有一个 context。以下是如何创建和使用 context 的步骤:
1. 创建一个 Context
使用 React.createContext
创建一个 context。
const ThemeContext = React.createContext('light'); // 'light' 是默认值
2. 为组件提供 Context 值
使用 Context.Provider
包裹你的组件,为其下层组件提供 context 值。
function App() {return (<ThemeContext.Provider value="dark"><Toolbar /></ThemeContext.Provider>);
}
在上述代码中,Toolbar
组件及其所有子组件都可以访问到 value 为 “dark” 的 context 值。
3. 在函数组件中使用 useContext
读取 Context 值
function Toolbar() {const theme = useContext(ThemeContext);return <div>{theme}</div>; // 输出 "dark"
}
在上述代码中,Toolbar
组件使用 useContext
Hook 来读取 ThemeContext
的当前值。
注意事项:
-
当 React 渲染使用
useContext
的组件时,它将使用离该组件最近的<Context.Provider>
的 value 值。如果没有找到Context.Provider
,则使用React.createContext
时传递的默认值。 -
记住,每当 Provider 的父组件重新渲染时,Provider 的子组件都将重新渲染,除非你通过使用
memo
或shouldComponentUpdate
对其进行了优化。
使用 useContext
,我们可以更方便地在函数组件中使用 context,避免了过去常常使用的消费者模式(Context.Consumer
)。
4. useReducer
:
一个替代 useState
的 Hook,适用于复杂的状态逻辑。
const [state, dispatch] = useReducer(reducer, initialArg, init);
useReducer
是 React 提供的一个 Hook,它是一个更为强大和灵活的状态管理机制,尤其适用于有复杂状态逻辑的场景。它的工作方式与 Redux 中的 reducer 很相似。
useReducer
接受两个参数:
- reducer:一个函数,它接受当前的 state 和一个 action,然后返回一个新的 state。
- initialState:初始状态值。
useReducer
返回一个数组,其中第一个元素是当前的 state,第二个元素是一个 dispatch 函数,你可以使用它来派发 action,以此触发 reducer 函数更新 state。
下面是如何使用 useReducer
的简单示例:
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 CounterApp() {// 使用 useReducerconst [state, dispatch] = useReducer(counterReducer, { count: 0 });return (<div>Count: {state.count}<button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button><button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button></div>);
}export default CounterApp;
在上面的示例中,我们定义了一个简单的 reducer,用于增加或减少计数器的值。然后在 CounterApp
组件中使用了 useReducer
,我们可以通过 dispatch 函数派发 'INCREMENT'
和 'DECREMENT'
的 action 来更改状态。
这种模式的一个关键好处是它使得组件的逻辑更清晰、更可预测,尤其在你的状态逻辑变得复杂时。此外,与 useState
相比,你可以将业务逻辑从 UI 逻辑中分离出来,这使得你的代码更容易测试和复用。
如果你熟悉 Redux,那么 useReducer
的工作方式会显得非常熟悉,因为它们都遵循相似的模式。但是,与 Redux 不同,useReducer
是完全内置于 React 中的,无需引入额外的库。
5. useCallback
:
返回一个记忆化的回调函数。
const increment = useCallback(() => {setCount(c => c + 1);
}, []);
useMemo
和 useCallback
都是 React 提供的 Hooks,旨在优化性能,特别是在有复杂操作或频繁渲染的场景中。它们的核心目的是帮助避免不必要的计算和重新渲染,但它们各自关注的点是不同的。
1. useMemo
useMemo
返回一个memoized 值。你可以将其看作一个记住了之前计算结果的函数。只有在其依赖项数组发生变化时,它才会重新计算这个值。
用途:当你有一个性能开销较大的计算,且这个计算只在某些特定变量改变时才需要重新进行时,可以使用 useMemo
。
const expensiveCalculation = (data) => {// ... 复杂的计算过程 ...return result;
};function Component(props) {const memoizedResult = useMemo(() => expensiveCalculation(props.data), [props.data]);return <div>{memoizedResult}</div>;
}
2. useCallback
useCallback
返回一个memoized 函数。它接受两个参数:一个函数和一个依赖项数组。它将返回该函数的 memoized 版本,该版本仅在依赖项发生更改时重新创建。
用途:当你传递回调函数给经常重新渲染的子组件或者给 useEffect
、useMemo
以及其他 hooks 时,并且你希望避免由于父组件重新渲染导致的不必要的子组件更新或效应的触发,你可以使用 useCallback
。
function ParentComponent() {const [count, setCount] = useState(0);const increment = useCallback(() => {setCount(count + 1);}, [count]);return <ChildComponent onIncrement={increment} />;
}
在上面的例子中,useCallback
确保只有当 count
发生更改时,increment
函数才会重新创建。这意味着如果 ParentComponent
因为其他原因重新渲染,但 count
没有变化,那么传递给 ChildComponent
的 onIncrement
回调将保持不变。
总结:
- useMemo:用于优化复杂计算。
- useCallback:用于优化函数引用,确保函数不会在每次渲染时都被重新创建。
虽然这些 hooks 可以提高性能,但它们并不总是必要的。在没有性能问题的情况下过度优化可能会导致代码更加复杂。建议在确定有性能瓶颈时再使用它们。
6. useMemo
:
返回一个记忆化的值。
const computedValue = useMemo(() => expensiveFunction(count), [count]);
7. useRef
:
返回一个可变的 ref 对象,它的 .current
属性被初始化为传递的参数。
const inputRef = useRef(null);
// 使用: <input ref={inputRef} />
useRef
是 React 中的一个 Hook,而 Vue 中的 refs 是 Vue 实例或组件的一个属性。尽管它们都与对 DOM 元素的直接引用有关,但它们在功能和使用方式上有所不同。
-
React 中的 useRef:
- 用于访问 DOM 元素或组件的引用。
- 返回一个可变的 ref 对象,其
.current
属性被初始化为传递给useRef()
的参数(默认为null
)。 - 常用于访问 DOM 节点,但也可以用作持久性变量的存储,即其值在组件的不同渲染之间保持不变。
- 当 ref 对象的内容发生变化时,不会触发组件重新渲染。
function FocusInput() {const inputRef = useRef(null);const handleClick = () => {inputRef.current.focus();};return (<><input ref={inputRef} type="text" /><button onClick={handleClick}>Focus the input</button></>); }
-
Vue 中的 refs:
- 在模板中,可以使用
ref
attribute 创建对 DOM 元素或子组件的引用。 - 通过
this.$refs
在组件实例上访问。 - 通常用于直接操作 DOM 或访问子组件的方法/属性。
- Vue 3 的 Composition API 中,你还可以在
setup
函数中使用ref
函数来创建一个响应式引用,但这与 DOM 引用的功能完全不同。
<template><input ref="myInput" type="text"><button @click="focusInput">Focus the input</button> </template><script> export default {methods: {focusInput() {this.$refs.myInput.focus();}} } </script>
- 在模板中,可以使用
总结:
-
React 的
useRef
可以用于访问 DOM 元素和持久变量,它返回一个对象,该对象的.current
属性指向引用的元素。 -
Vue 的 refs 主要用于在模板中创建对 DOM 元素或子组件的引用,并且可以通过
this.$refs
来访问。
这两者在概念上有所重叠,但在使用和功能上有所不同。特别是,Vue 中的 ref
还与 Vue 3 的 Composition API 中的响应性系统有关,这与其在 DOM 引用中的用途完全不同。
8. useLayoutEffect
:
与 useEffect
类似,但它在所有的 DOM 变化后同步触发。
useLayoutEffect(() => {// 读取或修改 DOM 等操作
});
9. useImperativeHandle
:
用于自定义暴露给父组件的实例值,当使用 forwardRef
时尤其有用。
10. useDebugValue
:
用于在 React DevTools 中显示自定义 Hook 的标签。
除了 React 核心提供的基础 Hooks 外,许多库和开发者还创建了自定义 Hooks 以提供额外的功能和逻辑重用。
Hooks 提供了一种更直观和功能强大的方式来使用函数组件,但在使用它们时应遵循一些规则,如不在循环、条件或嵌套函数中调用 Hooks。