React Hooks(实例及详解)

简介: 【7月更文挑战第2天】React Hooks(实例及详解)

React Hooks 是 React 16.8 版本引入的新特性,它们允许在函数组件中使用状态和其他 React 特性。以下是一些主要的

React Hooks:

useState: 是 React 的一个 Hook,它允许在函数组件中添加和管理状态。在此之前,只有类组件才能拥有自己的状态。useState 返回一个包含当前状态值和更新状态的函数的数组。
基本用法如下:

import React, { useState } from 'react';

function Example() {
// 声明一个新的状态变量,其初始值为 0
const [count, setCount] = useState(0);

return (


You clicked {count} times




);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在这个例子中:

const [count, setCount] = useState(0); 创建了一个新的状态变量 count 和一个用于更新这个状态的函数 setCount。

当用户点击按钮时,调用 setCount(count + 1) 更新状态。

状态改变会触发组件重新渲染,并且新的状态值将反映在界面上。

注意,setCount 函数不会立即修改状态,而是创建一个新的状态对象并安排组件进行重新渲染。这是因为在 React 中,所有的状态更新都是异步的。

你也可以传递一个函数给 setCount 来计算下一个状态值,这样可以确保使用的总是最新的状态:


1
2
3
此外,如果状态是复杂的数据结构(例如对象或数组),并且你需要根据现有的状态来创建一个新的状态,你应该使用函数形式的更新器以避免状态突变问题。

useEffect: 是 React 的一个 Hook,它允许在函数组件中执行副作用操作。这些副作用可能包括数据获取、订阅、手动更改 React 组件的 DOM 输出等。useEffect 可以看作是 componentDidMount, componentDidUpdate, 和 componentWillUnmount 生命周期方法的组合。
基本用法如下:

import React, { useState, useEffect } from 'react';

function Example() {
const [count, setCount] = useState(0);

// 使用 Effect Hook 来监听 count 状态的变化
useEffect(() => {
document.title = You clicked ${count} times;

return () => {
  // 清理工作(可选)
};

}, [count]); // 当依赖项数组中的值改变时,重新运行 effect

return (


You clicked {count} times




);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
在这个例子中:

useEffect 接收两个参数:一个包含要执行的副作用代码的函数和一个依赖项数组。

当组件渲染完成并挂载到 DOM 上后,useEffect 中的副作用函数会被调用一次。这类似于 componentDidMount 生命周期方法。

当依赖项数组中的值发生变化时,useEffect 中的副作用函数会再次被调用。这类似于 componentDidUpdate 生命周期方法。

如果指定了返回一个清理函数,那么当组件卸载或者依赖项变化导致重新计算 effect 时,这个清理函数会在下次 effect 运行之前被执行。这类似于 componentWillUnmount 生命周期方法。

注意,如果省略了第二个参数(依赖项数组),那么 effect 将只在组件挂载和卸载时运行,而不是每次渲染之后。这可以用来模拟 componentDidMount 和 componentWillUnmount 生命周期方法。

useContext: 是 React 的一个 Hook,它允许在函数组件中访问上下文对象。React 上下文 API 使得数据可以在组件树中传递而无需手动通过每个层级的 props。
基本用法如下:

import React, { createContext, useContext } from 'react';

// 创建一个新的 context 对象
const ThemeContext = createContext();

function App() {
return (



);
}

function Toolbar() {
return (




);
}

function ThemedButton() {
// 使用 useContext 获取当前的 theme 值
const theme = useContext(ThemeContext);

return ;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
在这个例子中:

首先创建了一个新的 context 对象 ThemeContext。

然后在 App 组件中使用 来提供上下文值。

在子组件 ThemedButton 中,我们使用 useContext(ThemeContext) 来获取当前的上下文值(即主题)。

useContext 返回的是当前 context 的值。当 context 的值发生变化时,所有订阅了这个 context 的组件都会重新渲染。

注意,如果你在没有包裹 的情况下使用 useContext,将会返回 context 的默认值。此外,为了避免无谓的重渲染,你应该尽可能地将 useContext 的调用放在最接近需要使用这些值的组件中。

useReducer: 是 React 的一个 Hook,它允许在函数组件中管理复杂的、可复用的逻辑。useReducer 接收一个 reducer 函数和初始状态作为参数,并返回当前的状态以及用于更新状态的 dispatch 方法。
基本用法如下:

import React, { useReducer } from 'react';

function reducer(state, action) {
switch (action.type) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
default:
throw new Error();
}
}

function Counter() {
const [state, dispatch] = useReducer(reducer, 0);

return (
<>
Count: {state}


</>
);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
在这个例子中:

我们首先定义了一个 reducer 函数,它接收两个参数:当前的状态和一个动作对象。根据动作类型,我们可以决定如何更新状态。

在 Counter 组件中,我们使用 useReducer 来创建一个新的状态变量(初始化为 0)和一个 dispatch 方法。dispatch 方法用于发送一个动作给 reducer,从而更新状态。

当用户点击按钮时,我们将对应的增加或减少的动作对象通过 dispatch 方法发送给 reducer,然后状态就会被相应地更新。

useReducer 可以替代多个 useState 调用,特别是当你的组件需要维护多个相关的状态值时。此外,由于 reducer 是纯函数,这使得测试变得更容易。

useCallback:` 是 React 的一个 Hook,它用于优化性能。当你在函数组件中使用内联回调(例如事件处理器)时,每次渲染都会创建一个新的函数实例。这可能会导致不必要的子组件重新渲染,即使它们没有依赖于这些回调。

useCallback 接收两个参数:一个需要 memoize 的回调函数和一个依赖项数组。当依赖项数组中的值发生变化时,返回的 memoized 回调函数将会更新;否则,它会返回先前缓存的版本。

基本用法如下:

import React, { useCallback } from 'react';

function Example({ a, b }) {
// 使用 useCallback 来记住计算的值
const memoizedCallback = useCallback(
() => {
console.log(a + b);
},
[a, b] // 当 a 或 b 变化时,重新计算
);

return (

);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在这个例子中:

我们定义了一个包含 a 和 b 的加法操作的回调函数,并通过 useCallback 返回了一个 memoized 版本。

如果 a 或 b 的值发生变化,useCallback 将返回一个新的 memoized 回调函数;否则,它将返回先前缓存的版本。

当用户点击按钮时,我们触发的是 memoized 回调函数。这样可以避免不必要的子组件重新渲染,因为除非 a 或 b 的值变化,否则回调函数不会改变。

注意,只有当你的回调函数被传递给子组件并且这个子组件执行了 shouldComponentUpdate() 或者使用了 React.memo() 时,useCallback 才能真正地提升性能。如果你的回调函数仅用于内部操作(如直接操作 DOM),那么使用 useCallback 不会带来任何好处。

useMemo:是 React 的一个 Hook,它用于优化性能。当你在函数组件中执行昂贵的计算或者创建复杂的对象时,每次渲染都会重新进行这些操作。这可能会导致不必要的性能开销。

useMemo 接收两个参数:一个需要 memoize 的值(通常是返回结果)和一个依赖项数组。当依赖项数组中的值发生变化时,返回的 memoized 值将会更新;否则,它会返回先前缓存的版本。

基本用法如下:

import React, { useMemo } from 'react';

function Example({ a, b }) {
// 使用 useMemo 来记住计算的值
const memoizedValue = useMemo(() => {
console.log('Computing expensive value...');
return a + b;
}, [a, b]); // 当 a 或 b 变化时,重新计算

return

{memoizedValue}
;
}
1
2
3
4
5
6
7
8
9
10
11
在这个例子中:

我们定义了一个包含 a 和 b 的加法操作,并通过 useMemo 返回了一个 memoized 版本。

如果 a 或 b 的值发生变化,useMemo 将返回一个新的 memoized 值;否则,它将返回先前缓存的版本。

每次渲染时,我们使用的是 memoized 值,而不是重新计算的结果。

注意,只有当你的计算结果被传递给子组件并且这个子组件执行了 shouldComponentUpdate() 或者使用了 React.memo() 时,useMemo 才能真正地提升性能。如果你的计算结果仅用于内部操作(如直接操作 DOM),那么使用 useMemo 不会带来任何好处。

useRef:是 React 的一个 Hook,它允许在函数组件中创建可变的引用。这个 Hook 返回一个 ref 对象,其 .current 属性被初始化为传递的参数(initialValue)。返回的对象在整个组件生命周期内保持不变。
基本用法如下:

import React, { useRef } from 'react';

function TextInputWithFocusButton() {
// 创建一个 ref 来保存文本输入框的 DOM 元素
const inputEl = useRef(null);

const onButtonClick = () => {
// 当按钮被点击时,使文本输入框获取焦点
inputEl.current.focus();
};

return (
<>


</>
);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在这个例子中:

我们通过调用 useRef() 创建了一个新的 ref,并将其赋值给 inputEl 变量。

在 标签上,我们将 inputEl 赋值给了 ref 属性。这样,React 将会把对应的 DOM 元素绑定到 inputEl.current 上。

当用户点击按钮时,我们可以通过调用 inputEl.current.focus() 来使文本输入框获取焦点。

注意,useRef() 不仅可以用于 DOM 元素,还可以用于任何类型的变量。它的主要用途是当需要在不同的渲染之间保留一个值或对象时。

useImperativeHandle: 是 React 的一个 Hook,它用于定制暴露给父组件的 ref 值。通常情况下,我们可以通过 ref 访问子组件内部的一些实例方法或属性。然而,在某些场景下,我们可能希望暴露一些特定的方法或属性,而不是默认的整个实例。
基本用法如下:

import React, { forwardRef, useImperativeHandle } from 'react';

function FancyInput(props, ref) {
const inputEl = useRef(null);

// 使用 useImperativeHandle 来暴露 customMethod 给父组件
useImperativeHandle(ref, () => ({
focus: () => {
inputEl.current.focus();
},
customMethod: () => {
console.log('I am a custom method');
}
}));

return ;
}

// 使用 forwardRef 将 ref 转发到 FancyInput 中
const WrappedFancyInput = forwardRef(FancyInput);

function ParentComponent() {
const fancyInputRef = useRef(null);

const handleClick = () => {
// 在父组件中调用子组件的 customMethod 方法
fancyInputRef.current.customMethod();
};

return (
<>


</>
);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
在这个例子中:

我们创建了一个名为 FancyInput 的函数组件,并使用了 forwardRef 高阶函数来接收一个 ref 参数。

在 FancyInput 内部,我们使用 useImperativeHandle 来定义要暴露给父组件的方法和属性。这里我们暴露了 focus 和 customMethod 方法。

父组件通过 ref 属性将 ref 对象传递给 WrappedFancyInput 子组件。这样,父组件就可以通过 fancyInputRef.current 访问到子组件暴露的 focus 和 customMethod 方法。

注意,过度使用 useImperativeHandle 可能会导致代码难以理解和维护。因此,你应该尽量避免直接操作子组件的 DOM,而是尽可能地使用 React 的数据流进行通信。

useLayoutEffect: 是 React 的一个 Hook,它类似于 useEffect,但有两个关键的区别:

useLayoutEffect 在所有 DOM 变更之后同步调用,但在浏览器绘制之前。这使得你可以读取布局并同步触发重新渲染。
在服务器端渲染中,useLayoutEffect 不会被执行。
基本用法如下:

import React, { useLayoutEffect } from 'react';

function Example() {
useLayoutEffect(() => {
// 这里可以访问到最新的 DOM 样式和几何信息
console.log('DOM 已经更新,但我还没有被绘制');
});

return

Example
;
}
1
2
3
4
5
6
7
8
9
10
在这个例子中:

当组件完成渲染时,useLayoutEffect 内的回调函数将会被执行。此时,所有的 DOM 更新都已经完成,但浏览器还没有开始绘制。

如果你需要在浏览器绘制之前访问或修改 DOM(例如设置样式),你应该使用 useLayoutEffect。

注意,过度使用 useLayoutEffect 可能会导致视觉闪烁或性能问题。因此,你应该尽量避免阻塞浏览器的渲染过程,并尽可能地使用标准的 useEffect 来处理副作用。

useDebugValue:是 React 的一个 Hook,它用于在 React 开发者工具中显示自定义 hook 的标签。这对于调试复杂的自定义 hook 很有用。
基本用法如下:

import React, { useDebugValue } from 'react';

function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);

// 获取 friendID 对应的好友在线状态
// ...

// 使用 useDebugValue 来为这个 hook 显示一个可读的标签
useDebugValue(isOnline ? '在线' : '离线');

return isOnline;
}

function FriendStatus() {
const isOnline = useFriendStatus(123456);

return (
{isOnline ? '在线' : '离线'}
);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在这个例子中:

我们创建了一个名为 useFriendStatus 的自定义 hook,它接收一个 friendID 参数,并返回好友的在线状态。

在 useFriendStatus 内部,我们使用 useDebugValue 来为这个 hook 显示一个可读的标签。这个标签将在 React 开发者工具的组件树中显示。

在 FriendStatus 组件中,我们调用了 useFriendStatus 并渲染了相应的在线状态。

注意,useDebugValue 只在开发模式下工作,并且只会影响 React 开发者工具中的展示。因此,在生产环境中,你不需要担心它会带来额外的性能开销。

这些是核心的 React Hooks,此外社区还开发了许多其他的自定义 Hooks,例如 useDebounce、useThrottle 等,以解决特定的问题和场景。

相关文章
|
2月前
|
前端开发 测试技术 开发工具
探索前端框架React Hooks的优势与应用
本文将深入探讨前端框架React Hooks的优势与应用。通过分析React Hooks的特性以及实际应用案例,帮助读者更好地理解和运用这一现代化的前端开发工具。
|
2月前
|
前端开发 JavaScript
react常用的hooks有哪些?
react常用的hooks有哪些?
44 0
|
13天前
|
缓存 前端开发 JavaScript
React Hooks 一步到位
React Hooks 一步到位
|
2月前
|
存储 前端开发 JavaScript
React Hooks 的替代方案有哪些?
【5月更文挑战第28天】React Hooks 的替代方案有哪些?
29 2
|
2月前
|
前端开发 JavaScript 开发者
React Hooks 的应用场景有哪些?
【5月更文挑战第28天】React Hooks 的应用场景有哪些?
19 1
|
2月前
|
前端开发 JavaScript 开发者
React Hooks 是在 React 16.8 版本中引入的一种新功能
【5月更文挑战第28天】React Hooks 是在 React 16.8 版本中引入的一种新功能
30 1
|
2月前
|
前端开发
React Hooks - useState 的使用方法和注意事项(1),web前端开发前景
React Hooks - useState 的使用方法和注意事项(1),web前端开发前景
|
2月前
|
前端开发
探索React Hooks:一种全新的组件逻辑管理方式
React Hooks是React 16.8版本引入的一项新功能,它改变了我们编写React组件的方式。本文将从Hooks的起源讲起,逐步分析Hooks的优势,并通过具体示例展示Hooks在组件逻辑管理中的应用,旨在帮助读者更好地理解和运用React Hooks。
|
2月前
|
前端开发 API 开发者
React Hooks API:自定义Hooks的创建与使用
【4月更文挑战第25天】本文介绍了React自定义Hooks的创建与使用。自定义Hooks是提升React开发效率的关键工具。
|
2月前
|
前端开发 JavaScript
React Hooks:让你轻松掌握函数组件的状态与管理
React Hooks:让你轻松掌握函数组件的状态与管理