跳到主要内容

Hooks

React Hooks 是什么?

React Hooks 是对 React function 组件的一种扩展,通过一些特殊的函数,让无状态组件拥有状态组件才拥有的能力。

Hooks 是 React 函数组件中的一类特殊函数,通常以 use 开头,比如 useRef,useState,useReducer 等。通常在我们写 React 组件的时候,如果这个组件比较复杂,拥有自己的生命周期或者 state,就将其写成 class 组件;如果这个组件仅仅用来展示,就将其写成 function 组件。

React Hooks 使用 function 组件的写法,通过 useState 这样的 API 解决了 function 组件没有 state 的问题,通过 useEffect 来解决生命周期的问题,通过自定义 hooks 来复用业务逻辑。

下面介绍几个常用的 Hook,更多详情看官方文档:https://zh-hans.react.dev/reference/react

useState

useState 是一个 Hook,用于在函数组件中添加局部状态。你可以通过调用 useState 来声明和初始化状态变量,并获取状态的当前值和一个更新状态的函数。

import React, { useState } from 'react';

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

return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

useEffect

useEffect 是 React 中一个非常重要的 Hook,它用于处理副作用操作,如数据获取、DOM 操作、订阅等。具体而言,useEffect 的主要作用是在函数组件中管理生命周期和副作用。它接受一个函数作为参数,该函数包含了副作用操作的逻辑,同时还可以接受一个依赖数组,用于控制何时触发副作用。

以下是 useEffect 的一些常见用途和场景:

依赖数组

在使用 useEffect 时,可以传递一个依赖数组,它告诉 React 该副作用应该在哪些状态变化时触发。

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

function Counter() {
const [count, setCount] = useState(0);
const [message, setMessage] = useState('');

useEffect(() => {
// 这个函数将在 count 或 message 改变时触发
console.log('Effect triggered');

// 在每次 effect 执行时更新页面标题
document.title = `Count: ${count}`;
}, [count, message]); // 依赖数组包含 count 和 message

return (
<div>
<h1>Count: {count}</h1>
<p>{message}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setMessage('Hello, React!')}>Set Message</button>
</div>
);
}

export default Counter;

在上面的例子中,我们使用 useEffect 来更新页面标题,以反映 count 的值,并且在 message 改变时也触发。useEffect 的依赖数组包含了 count 和 message,这意味着只有当这两个状态发生变化时,useEffect 才会执行。如果两者都保持不变,useEffect 将不会重复触发。

数据获取

可以使用 useEffect 在组件挂载后获取数据,然后将数据更新到组件的状态中。这允许你在组件渲染之前获取所需的数据。

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

function DataFetcher() {
const [data, setData] = useState(null);

useEffect(() => {
fetchData()
.then((result) => setData(result))
.catch((error) => console.error(error));
}, []); // 依赖数组为空,表示只在组件挂载时执行

return (
<div>
{data ? <p>Data: {data}</p> : <p>Loading...</p>}
</div>
);
}

DOM 操作

你可以在 useEffect 中执行 DOM 操作,例如添加、删除或更新 DOM 元素。这通常在组件挂载后或特定状态更改后执行。

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

function DynamicText() {
const [text, setText] = useState('Hello, React');

useEffect(() => {
const interval = setInterval(() => {
setText((prevText) => prevText + '!');
}, 1000);

return () => {
clearInterval(interval);
};
}, []); // 依赖数组为空,表示只在组件挂载时执行

return <p>{text}</p>;
}

订阅和取消订阅

当你需要在组件订阅外部事件或资源时,可以在 useEffect 中进行订阅,并在组件卸载时取消订阅,以避免内存泄漏。

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

function EventSubscriber() {
const [data, setData] = useState(null);

useEffect(() => {
const subscription = subscribeToData((result) => {
setData(result);
});

return () => {
// 在组件卸载时取消订阅
subscription.unsubscribe();
};
}, []); // 依赖数组为空,表示只在组件挂载时执行

return (
<div>
{data ? <p>Data: {data}</p> : <p>Waiting for data...</p>}
</div>
);
}

这个 subscribeToData 是一个示例中用来模拟数据订阅的函数。它在实际应用中可能代表与外部数据源建立连接或订阅数据更新的操作,例如 WebSocket 连接、服务器端事件 (Server-Sent Events)、某个状态管理库的事件订阅等。如下所示

function subscribeToData(callback) {
// 模拟数据源,每隔一秒钟生成一个随机数并传递给回调函数
const interval = setInterval(() => {
const data = Math.random();
callback(data);
}, 1000);

// 返回一个对象,用于取消订阅
return {
unsubscribe: () => {
clearInterval(interval);
},
};
}

管理生命周期

useEffect 也可以模拟生命周期方法,例如 componentDidMount、componentDidUpdate 和 componentWillUnmount。通过在 useEffect 的依赖数组中添加特定的状态变量,你可以控制副作用操作的触发时机。

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

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

useEffect(() => {
console.log('Component mounted');

return () => {
console.log('Component unmounted');
};
}, []); // 依赖数组为空,表示只在组件挂载和卸载时执行

useEffect(() => {
console.log('Count updated');
}, [count]); // 当 count 变化时执行

return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

useQuery

useQuery 是来自 React Query 库的一个钩子(hook),它用于在 React 应用中进行数据获取、缓存、同步和更新。React Query 提供了一种简单、强大的方式来处理异步数据,特别是在与 API 交互时。下面是 useQuery 的一些主要特点和用法:

  1. 自动数据获取与缓存useQuery 自动从 API 或其他异步数据源获取数据,并将结果缓存起来,以便在多个组件之间共享。

  2. 后台数据更新:它能够在数据过期或窗口重新获取焦点时,在后台更新数据。

  3. 状态管理useQuery 提供了数据的加载、错误和成功状态,使得在 UI 中处理这些状态变得更加容易。

  4. 自动重试和轮询:支持失败后自动重试和定期轮询数据。

这里介绍一下它的基本用法

const { data, isLoading, error } = useQuery(queryKey, fetchFunction, options);
  • queryKey:一个唯一标识符,用于缓存和追踪查询状态。
  • fetchFunction:一个异步函数,用于获取数据。
  • options:可选参数,用于配置查询的行为(例如:重试次数、刷新间隔等)。

假设你有一个函数 fetchUser 用于从 API 获取用户数据,你可以这样使用 useQuery

const { data, isLoading, error } = useQuery('user', fetchUser);

这里,'user' 是查询的键,用于唯一标识这个查询。fetchUser 是获取数据的函数。data 存储着查询结果,isLoadingerror 分别表示加载状态和错误信息。

React Query 通过简化数据获取和缓存的复杂性,帮助开发者更高效地构建响应式和健壮的应用。

自定义 Hooks

你可以创建自定义 Hooks 来封装可重用的逻辑,使其在多个组件之间共享。

import { useState, useEffect } from 'react';

function useDataFetching(url) {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
fetchData(url)
.then((result) => {
setData(result);
setIsLoading(false);
})
.catch((error) => {
console.error(error);
setIsLoading(false);
});
}, [url]);
return { data, isLoading };
}

然后,你可以在多个组件中使用自定义 Hook,以获取和处理数据。