React 提供了许多常用的 Hooks,用于在函数组件中添加状态管理、副作用处理和其他功能,下面介绍其中几个常用的,React还有很多其他hooks。
useState:
useSate用于在函数组件中添加状态管理。它返回一个状态值和更新该状态值的函数。
使用时一些特点和注意事项
-
正确定义初始状态:在使用
useState
时,需要为初始状态提供一个合适的值。确保初始状态的类型与后续状态的类型保持一致,避免出现类型错误。 -
解构数组:
useState
返回一个包含状态值和更新状态值的函数的数组,通常可以使用数组解构来获取它们。例如:const [state, setState] = useState(initialState);
。这样做可以提高代码的可读性。 -
更新状态:使用
setState
函数来更新状态值。注意,setState
并不会像类组件中的setState
方法一样自动合并更新,而是替换整个状态对象。因此,在更新状态时,请确保包含了所有需要保留的状态值,而不仅仅是更新的部分。 -
异步更新:由于状态更新是异步的,多次连续调用
setState
不会立即反映到状态值上。如果需要基于先前的状态进行更新,可以使用回调函数的形式调用setState
。例如:setState(prevState => prevState + 1);
。 -
函数式更新:当新的状态值依赖于先前的状态值时,可以使用函数式更新形式。这样可以避免依赖于当前状态的值,并确保获取到最新的状态。例如:
setState(prevState => prevState + 1);
。
import { Button } from 'antd';
import React, { useState } from 'react';
const Test = (props: any) => {
const [count, setCount] = useState(0);
const onChange = () => {
setCount(count + 1);
// 异步更新有时候会不生效,需要这样写
setCount(pre => pre + 1);
}
return (
<div>
{count}
<Button onClick={onChange}>点击</Button>
</div>
)
}
export default Test;
useEffect
用于处理副作用,例如数据获取、订阅、事件处理等。它在组件渲染完成后执行,并可以在组件更新时重新执行。
useEffect
是 React 中常用的 Hook 之一,用于处理副作用操作,例如数据获取、订阅、事件监听等。useEffect
在组件渲染后执行,并可以在组件更新时重新执行。
useEffect
接受两个参数:一个副作用函数和一个依赖项数组。
副作用函数是在组件渲染后执行的函数。它可以包含任何副作用操作,例如订阅事件、发送网络请求、操作 DOM 等。该函数可以返回一个清理函数,用于在组件卸载或重新渲染之前执行清理操作。
依赖项数组是一个可选的参数,用于指定副作用函数依赖的值。当依赖项数组发生变化时,useEffect
将重新执行副作用函数。如果不提供依赖项数组,副作用函数将在每次组件更新时都执行。如果传入一个空数组 []
,副作用函数将只在组件首次渲染后执行,不会重新执行。
以下是 useEffect
的基本用法:
import { useEffect } from 'react';
function MyComponent() {
// 无依赖项,会一值执行
useEffect(() => {
// 操作
});
// 依赖项为空,组件初始化会执行一次
useEffect(() => {
// 操作
},[]);
// 依赖于某个变量或函数,在依赖项发生变化后触发
useEffect(() => {
// 在组件渲染后执行副作用操作
// 返回一个清理函数(可选)
return () => {
// 在组件卸载或重新渲染之前执行清理操作
//该清理函数将在组件卸载或重新渲染之前执行
};
}, [/* 依赖项数组 */]);
return (
<div></div>
);
}
下面是一些关于 useEffect
的常见用法和注意事项:
-
数据获取和订阅:可以在
useEffect
中进行异步操作,例如发起网络请求获取数据或订阅事件。确保在清理函数中取消订阅或中断请求,以避免内存泄漏。 -
依赖项数组的使用:通过依赖项数组,可以控制副作用函数的执行时机。只有当依赖项发生变化时,才会重新执行副作用函数。如果不提供依赖项数组,则副作用函数将在每次组件更新时都执行。
-
空依赖项数组的使用:如果副作用函数不依赖任何状态或属性,可以传入一个空数组
[]
,使副作用函数仅在组件首次渲染后执行一次。 -
清理函数的使用:如果副作用函数需要进行清理操作,例如取消订阅或清除定时器,请在副作用函数中返回一个清理函数。该清理函数将在组件卸载或重新渲染之前执行。
-
异步操作和更新状态:在副作用函数中进行异步操作时,确保正确处理状态的更新。使用函数式更新或通过依赖项数组传入更新的状态。
useContext
当使用 useContext
Hook 时,可以方便地在 React 组件中访问全局的上下文数据。下面是一个使用 useContext
的简单例子:
首先,创建一个上下文对象:
import React, { createContext } from 'react';
// 创建上下文对象
const MyContext = createContext();
// 上下文提供器
function MyContextProvider({ children }) {
const sharedData = 'Shared Data';
return (
<MyContext.Provider value={sharedData}>
{children}
</MyContext.Provider>
);
}
在上面的例子中,创建了一个名为 MyContext
的上下文对象,并通过 MyContext.Provider
提供器将共享数据 sharedData
传递给子组件。
然后,在需要访问上下文数据的组件中使用 useContext
:
import React, { useContext } from 'react';
function MyComponent() {
const sharedData = useContext(MyContext);
return (
<div>
Shared Data: {sharedData}
</div>
);
}
使用 useContext
Hook 来获取 MyContext
上下文的值,并将其赋值给 sharedData
变量。然后,可以在组件中使用该值进行渲染或其他操作。
注意事项:
-
使用
useContext
前,确保已在组件树中的某个地方提供了上下文。在上面的例子中,通过MyContext.Provider
提供器在组件树中提供了上下文数据。 -
上下文数据的更新:当上下文数据发生变化时,使用
useContext
的组件会自动重新渲染。这意味着,当共享数据发生更改时,使用该上下文的所有组件都会更新。 -
上下文嵌套:React 允许上下文进行嵌套。在嵌套的情况下,使用
useContext
会获取最接近的上层提供器的值。 -
上下文的性能优化:当上下文数据较大或频繁变化时,可以使用
React.memo
、useMemo
或自定义的优化方法来优化上下文的性能。 -
避免过度使用上下文:上下文是用于共享数据的有用工具,但过度使用可能导致组件之间的耦合性增加。请在需要共享数据的组件之间仔细考虑使用上下文的合适程度。
使用 useContext
可以方便地访问和共享上下文数据,但请确保在使用前提供了上下文,并注意上述的注意事项,以确保正确使用上下文功能。
useReducer
useReducer
是 React 中的一个常用 Hook,用于管理具有复杂状态逻辑的组件。它类似于 Redux 中的 reducer,接收一个状态和操作函数,并返回新的状态和派发操作的函数。
使用 useReducer
时,需要定义一个 reducer 函数和初始状态。
reducer 函数接收当前状态和一个操作(action),并根据操作的类型来更新状态。它返回更新后的状态。reducer 函数的定义类似于 Redux 中的 reducer:
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
}
初始状态是状态的初始值:
const initialState = { count: 0 };
然后,使用 useReducer
Hook 在组件中应用 reducer:
import React, { useReducer } from 'react';
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
</div>
);
}
在上述例子中,我们使用 useReducer
定义了状态 state
和派发操作的函数 dispatch
。我们可以根据操作的类型通过调用 dispatch
函数来派发操作,并由 reducer 函数来更新状态。
useReducer
在处理具有复杂状态逻辑的组件时非常有用。以下是一些使用场景:
- 计数器:可以使用
useReducer
来管理计数器的状态和操作,例如增加、减少计数等。 - 表单处理:使用
useReducer
可以更好地管理表单的状态和用户输入的操作,以及进行表单验证等复杂逻辑。 - 数据列表:当处理复杂的数据列表时,使用
useReducer
可以更好地管理数据的加载、筛选、排序等操作,以及处理分页等功能。
总的来说,useReducer
适用于需要管理复杂状态逻辑的组件,并且可以帮助组织和更新状态,以及处理相关的操作。它可以代替使用 useState
的方式,特别适合管理具有多个操作类型和相关状态的组件。
useMemo
useMemo
是 React 中的一个常用 Hook,用于在组件渲染过程中进行性能优化,避免不必要的计算和重复渲染。
useMemo
接受两个参数:一个计算函数和依赖项数组。它会在组件渲染过程中执行计算函数,并将计算结果缓存起来。只有当依赖项数组中的值发生变化时,才会重新执行计算函数。
使用 useMemo
可以避免在每次渲染时重复计算耗时的操作,并且可以根据依赖项的变化来更新计算结果。以下是一些 useMemo
的常见使用场景:
-
计算结果的缓存:当需要根据某些输入计算结果时,可以使用
useMemo
缓存计算结果,避免重复计算。这在计算量较大的场景下特别有用。 -
避免不必要的重渲染:当一个组件依赖于某个状态或属性,但这些状态或属性的变化并不会影响到组件的渲染结果时,可以使用
useMemo
来缓存渲染结果,避免不必要的重渲染。 -
优化子组件的渲染:当将一个计算结果作为属性传递给子组件时,可以使用
useMemo
缓存计算结果,以避免在每次父组件渲染时都重新计算并传递给子组件。 -
性能敏感的比较操作:当需要进行一些性能敏感的比较操作,例如深度比较对象或数组时,可以使用
useMemo
缓存比较结果,以避免在每次渲染时重新执行比较操作。
下面是一个使用useMemo
的简单例子,展示如何缓存计算结果以提高性能:
import React, { useMemo, useState } from 'react';
function ExpensiveComponent() {
// 假设这里有一个计算耗时的函数
function calculateExpensiveValue() {
// ... 复杂的计算逻辑 ...
console.log('Calculating expensive value...');
return Math.random();
}
// 使用 useMemo 缓存计算结果
const expensiveValue = useMemo(() => calculateExpensiveValue(), []);
return (
<div>
Expensive Value: {expensiveValue}
</div>
);
}
function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ExpensiveComponent />
</div>
);
}
在上述代码中,有一个 ExpensiveComponent
组件,其中包含一个计算耗时的函数 calculateExpensiveValue
。为了避免在每次渲染时都重新计算 expensiveValue
,使用 useMemo
缓存计算结果。
通过将 calculateExpensiveValue
函数作为 useMemo
的第一个参数,并将空数组作为依赖项传递给 useMemo
,确保只在组件首次渲染时执行一次计算,并将结果缓存起来。当 count
发生变化时,ExpensiveComponent
重新渲染,但不会触发计算函数的重新执行。
useCallBack
在 React 中,useCallback
是一个用于优化性能的 hooks 函数。它用于在函数组件中缓存函数引用,避免在每次渲染时都创建新的函数实例。这在处理传递给子组件的回调函数时特别有用,因为子组件可能会在每次渲染时都接收新的回调,导致子组件不必要的重新渲染。
useCallback
接收两个参数:回调函数和一个依赖数组。它将返回一个经过优化的回调函数,该函数只有在依赖数组中的依赖项发生变化时才会重新创建。如果依赖项不变,useCallback
将返回之前缓存的回调函数引用,从而避免不必要的函数创建。
以下是一些常见的使用场景:
-
传递给子组件的回调函数:当您将回调函数作为属性传递给子组件时,可以使用
useCallback
来缓存回调函数,以避免不必要的子组件重新渲染。这在大型应用中特别有用,其中有许多子组件需要接收回调函数作为属性。 -
避免额外的渲染:如果回调函数依赖于组件的某些状态或属性,但实际上并不需要在每次组件渲染时都创建新的函数实例,您可以使用
useCallback
并将相关依赖项添加到依赖数组中,以避免不必要的渲染。 -
使用在 useEffect 和 useMemo 中:当您将回调函数用作
useEffect
或useMemo
的依赖项时,也可以使用useCallback
来缓存回调函数,以确保依赖项在变化时能够得到更新。 -
优化性能:在大型应用中,如果有许多回调函数需要频繁创建,使用
useCallback
可以帮助优化性能,减少不必要的函数创建,提高应用的性能和响应性。
使用 useCallback
的基本语法如下:
import React, { useCallback } from 'react';
const MyComponent = ({ onButtonClick }) => {
// 使用 useCallback 缓存回调函数
const handleClick = useCallback(() => {
// 执行点击处理逻辑
// ...
onButtonClick(); // 调用传递进来的回调函数
}, [onButtonClick]); // 依赖数组中包含了传递进来的回调函数
return (
<div>
<button onClick={handleClick}>Click Me</button>
</div>
);
};
在上面的示例中,使用 useCallback
来缓存 handleClick
回调函数。传递给 useCallback
的依赖数组 [onButtonClick]
表示只有当 onButtonClick
发生变化时,handleClick
才会重新创建。
如果不使用 useCallback
,每次 MyComponent
组件重新渲染时,handleClick
都会被重新创建,即使 onButtonClick
没有发生变化。通过使用 useCallback
,可以确保只有在 onButtonClick
发生变化时,handleClick
才会重新创建,从而避免子组件不必要的重新渲染。
useCallback
在性能优化方面是非常有用的,尤其是在大型应用中,当有大量的回调函数需要传递给子组件时。它可以帮助减少不必要的组件重新渲染,提高应用的性能和响应性。
useLayoutEffect
useLayoutEffect
是 React 中的一个 Hook,与 useEffect
类似,但它在 DOM 变更之后同步执行,而不是在浏览器绘制之后执行。它会在浏览器布局和绘制之前同步执行回调函数。
使用 useLayoutEffect
可以在浏览器布局完成后立即执行一些操作,以确保获取到最新的 DOM 布局信息,并在下一次渲染之前同步地更新 UI。这在需要准确地测量 DOM 元素的尺寸、位置或进行 DOM 操作时非常有用。
下面是一个使用 useLayoutEffect
的简单例子:
import React, { useRef, useLayoutEffect } from 'react';
function MeasureElement() {
const ref = useRef();
useLayoutEffect(() => {
// 在浏览器布局完成后立即执行操作
const element = ref.current;
const { width, height } = element.getBoundingClientRect();
// 使用测量结果进行操作
console.log('Element size:', width, height);
}, []);
return <div ref={ref}>Measure me!</div>;
}
function App() {
return (
<div>
<MeasureElement />
</div>
);
}
在上述例子中,创建了一个名为 MeasureElement
的组件,在组件内部使用了 useLayoutEffect
。在 useLayoutEffect
的回调函数中,我们可以获取到被测量元素的最新布局信息,并进行相应的操作。
在这个例子中,我们使用 getBoundingClientRect
方法获取被测量元素的宽度和高度,并将结果打印到控制台。这个操作在浏览器布局完成后同步执行,确保我们获取到的尺寸是最新的。
需要注意的是,由于 useLayoutEffect
在浏览器布局之后同步执行,因此它的执行会阻塞浏览器的渲染过程。因此,只有在需要同步更新 UI 或测量 DOM 元素时才使用 useLayoutEffect
,避免造成性能问题。