useRequest
是一个自定义的 React Hook,用于简化异步请求逻辑,封装了请求、响应处理、错误和加载状态管理等功能。通过使用 useRequest
,开发者可以更专注于业务逻辑,减少代码冗余,提高开发效率。它提供了多种配置选项,如请求方法、请求头、超时设置等,增强了代码的复用性和可维护性。
useRequest
是一个自定义的 React Hook,用于简化异步请求逻辑在组件中的编写。它封装了常见的请求操作,如发起请求、处理响应、错误处理以及加载状态管理,使得开发者能够更加专注于业务逻辑,而无需关心底层的实现细节。通过使用 useRequest
,我们可以更方便地进行 API 调用,减少代码冗余,提高开发效率。
useRequest 的作用和优势
useRequest
的主要作用是封装异步请求逻辑,使组件更加简洁和易于维护。具体来说,它提供了以下优势:
- 简化代码:通过封装请求逻辑,
useRequest
减少了对通用请求处理代码的重复编写,提高了代码的复用性和可维护性。 - 提升性能:内置的缓存和取消功能可以帮助优化请求性能,避免不必要的网络请求或重复的计算。
- 增强用户体验:通过加载状态管理和错误处理,可以更好地控制用户界面的交互,提供流畅的用户体验。
- 统一的 API 设计:使用
useRequest
可以制定统一的请求处理规范,使得团队中的其他成员更容易理解和使用。
在开始使用 useRequest
之前,我们首先需要安装相关的依赖包。useRequest
可以通过 npm 或 yarn 进行安装。这里以 npm 为例,执行以下命令来安装 @umijs/hooks
包,该包中包含了许多自定义的 React Hooks,包括 useRequest
:
npm install @umijs/hooks
或者使用 yarn:
yarn add @umijs/hooks
安装完成后,在项目中引入 useRequest
:
import { useRequest } from '@umijs/hooks';
接下来,我们可以在 React 组件中使用 useRequest
来发起请求和处理响应。
创建 useRequest Hook
要使用 useRequest
,我们需要在组件内部定义一个 useRequest
Hook。useRequest
接受一个 API 函数作为输入,该函数用于发起实际的网络请求。下面是一个简单的例子,演示如何创建并使用 useRequest
:
import React from 'react';
import { useRequest } from '@umijs/hooks';
function MyComponent() {
const { data, loading, error, run } = useRequest(fetchData);
return (
<div>
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
{data && <div>Data: {data.message}</div>}
<button onClick={run}>Fetch Data</button>
</div>
);
}
function fetchData() {
return fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => data);
}
发送请求并处理响应
在上面的例子中,useRequest
返回了一个对象,该对象包含了 data
、loading
、error
和 run
等属性:
data
:请求成功后的响应数据。loading
:一个布尔值,指示当前请求是否正在进行中。error
:如果请求失败,将包含错误信息。run
:一个函数,用于手动触发请求。
通过 data
、loading
和 error
,我们可以轻松地控制组件的渲染状态。而 run
函数允许我们根据需要手动发起请求,例如,可以将其绑定到按钮点击事件上。
错误处理
错误处理是 useRequest
的一个重要特性。当请求失败时,useRequest
会将错误信息存储在 error
属性中。我们可以根据 error
的存在与否来显示不同的 UI 状态。下面是一个示例,演示如何在组件中处理请求错误:
import React from 'react';
import { useRequest } from '@umijs/hooks';
function MyComponent() {
const { data, loading, error, run } = useRequest(fetchData);
return (
<div>
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
{data && <div>Data: {data.message}</div>}
<button onClick={run}>Fetch Data</button>
</div>
);
}
function fetchData() {
return fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => data)
.catch(error => {
throw new Error('Request failed with status: ' + error.status);
});
}
在上述代码中,fetchData
函数在请求失败时会抛出一个包含错误状态码的错误对象。在 useRequest
中捕获此错误,并将其存储在 error
属性中。因此,我们可以在渲染组件时根据 error
的存在与否来显示错误信息。
加载状态管理
useRequest
通过 loading
属性来指示请求是否正在进行中。通过检查 loading
的值,我们可以为用户显示一个加载指示器或加载中的文本。
下面是一个示例,展示了如何使用 loading
来控制组件的加载状态:
import React from 'react';
import { useRequest } from '@umijs/hooks';
function MyComponent() {
const { data, loading, error, run } = useRequest(fetchData);
return (
<div>
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
{data && <div>Data: {data.message}</div>}
<button onClick={run}>Fetch Data</button>
</div>
);
}
function fetchData() {
return fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => data);
}
在这个例子中,当 loading
为 true
时,会显示一个加载指示器。一旦请求完成,loading
会被更新为 false
,加载指示器将不再显示。
自定义请求配置
我们可以向 useRequest
提供更多的配置选项来定制请求行为。这些配置通常包括请求方法、请求头、超时设置等。useRequest
允许我们传递一个配置对象给它,这会覆盖默认配置。
下面是一个示例,展示了如何使用配置对象来自定义请求:
import React from 'react';
import { useRequest } from '@umijs/hooks';
function MyComponent() {
const { data, loading, error, run } = useRequest(fetchData, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'value' })
});
return (
<div>
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
{data && <div>Data: {data.message}</div>}
<button onClick={run}>Fetch Data</button>
</div>
);
}
function fetchData(config) {
return fetch('https://api.example.com/data', config)
.then(response => response.json())
.then(data => data);
}
在这个例子中,我们通过第二个参数传递了一个配置对象,该对象设置了请求方法为 POST,并指定了请求头和请求体。这些配置将被传递给 fetch
函数,从而自定义请求行为。
使用请求的缓存和取消功能
useRequest
还提供了一些高级功能,如请求缓存和取消。通过启用缓存,我们可以避免在短时间内重复发起相同的请求,从而提升性能。而取消功能则允许我们在组件卸载或页面跳转时取消正在进行的请求。
下面是一个示例,展示了如何启用缓存和取消功能:
import React, { useEffect } from 'react';
import { useRequest } from '@umijs/hooks';
function MyComponent() {
const { data, loading, error, run, cancel } = useRequest(fetchData, {
cacheKey: 'myData',
shouldCache: false,
cancelToken: new CancelToken(cancel => {})
});
useEffect(() => {
run();
return cancel;
}, [run, cancel]);
return (
<div>
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
{data && <div>Data: {data.message}</div>}
<button onClick={run}>Fetch Data</button>
</div>
);
}
function fetchData(config) {
return fetch('https://api.example.com/data', config)
.then(response => response.json())
.then(data => data);
}
在这个例子中,我们使用了 cacheKey
和 shouldCache
配置来控制缓存行为。我们还传递了一个取消令牌,以便在组件卸载时取消请求。
使用 useRequest 实现用户登录功能
用户登录是一个常见的场景,涉及到异步请求和状态管理。我们可以使用 useRequest
来简化这个过程。下面是一个示例,展示了如何使用 useRequest
来实现用户登录功能:
import React, { useState } from 'react';
import { useRequest } from '@umijs/hooks';
function LoginForm() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const { data, loading, error, run } = useRequest(login, {
manual: true
});
useEffect(() => {
if (data && data.success) {
console.log('Login successful');
} else if (data && !data.success) {
console.log('Login failed');
}
}, [data]);
const handleSubmit = () => {
if (username && password) {
run({ username, password });
} else {
console.log('Please enter both username and password');
}
};
return (
<div>
<input
type="text"
placeholder="Username"
value={username}
onChange={e => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
<button onClick={handleSubmit}>Login</button>
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
{data && data.success && <div>Login successful!</div>}
{data && !data.success && <div>Login failed!</div>}
</div>
);
}
function login({ username, password }) {
return fetch('https://api.example.com/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
})
.then(response => response.json())
.then(data => data);
}
在上述代码中,我们定义了一个 LoginForm
组件,该组件使用 useRequest
来发起用户登录请求。我们通过 manual: true
参数指定 run
函数应该手动触发。当用户提交表单时,handleSubmit
函数会调用 run
函数来发起登录请求。请求的结果会通过 data
属性返回,并根据登录结果更新 UI 状态。
使用 useRequest 实现数据列表的请求和更新
另一个常见的场景是请求和更新数据列表。我们可以通过 useRequest
来实现这个功能。下面是一个示例,展示了如何使用 useRequest
来请求和更新数据列表:
import React from 'react';
import { useRequest } from '@umijs/hooks';
function DataList() {
const { data, loading, error, run } = useRequest(fetchData, {
refreshDeps: [query]
});
const [query, setQuery] = useState('');
useEffect(() => {
run({ query });
}, [query]);
const handleQueryChange = e => {
setQuery(e.target.value);
};
return (
<div>
<input
type="text"
placeholder="Search"
value={query}
onChange={handleQueryChange}
/>
<div>
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
{data && data.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
</div>
);
}
function fetchData({ query }) {
return fetch(`https://api.example.com/data?query=${query}`)
.then(response => response.json())
.then(data => data.items);
}
在上述代码中,我们定义了一个 DataList
组件,该组件使用 useRequest
来请求数据列表。我们通过 refreshDeps
参数来指定依赖项,这表示当 query
发生变化时,请求将被重新触发。当用户在搜索框中输入查询内容时,handleQueryChange
函数会更新 query
状态,从而触发一个新的请求。请求的结果会通过 data
属性返回,并根据请求结果更新 UI 状态。
使用 useRequest 实现复杂的数据列表请求和更新
本节提供了一个更复杂的示例,展示了如何使用 useRequest
处理分页、排序和更复杂的用户交互:
import React, { useState, useEffect } from 'react';
import { useRequest } from '@umijs/hooks';
function AdvancedDataList() {
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [sortOrder, setSortOrder] = useState('asc');
const { data, loading, error, run } = useRequest(fetchData, {
refreshDeps: [currentPage, pageSize, sortOrder]
});
useEffect(() => {
run({ currentPage, pageSize, sortOrder });
}, [currentPage, pageSize, sortOrder]);
const handlePageChange = newPage => {
setCurrentPage(newPage);
};
const handlePageSizeChange = newSize => {
setPageSize(newSize);
};
const handleSortChange = newOrder => {
setSortOrder(newOrder);
};
return (
<div>
<button onClick={() => handlePageChange(1)}>First Page</button>
<button onClick={() => handlePageChange(currentPage - 1)}>Prev Page</button>
<button onClick={() => handlePageChange(currentPage + 1)}>Next Page</button>
<button onClick={() => handlePageChange(Math.ceil(data.total / pageSize))}>Last Page</button>
<select value={pageSize} onChange={e => handlePageSizeChange(e.target.value)}>
<option value="10">10</option>
<option value="20">20</option>
<option value="50">50</option>
</select>
<select value={sortOrder} onChange={e => handleSortChange(e.target.value)}>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
<div>
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
{data && data.items.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
</div>
);
}
function fetchData({ currentPage, pageSize, sortOrder }) {
const urlParams = new URLSearchParams({
page: currentPage,
size: pageSize,
sort: sortOrder
}).toString();
return fetch(`https://api.example.com/data?${urlParams}`)
.then(response => response.json())
.then(data => data);
}
在这个例子中,我们定义了一个 AdvancedDataList
组件,该组件使用 useRequest
来请求数据列表。组件通过 currentPage
、pageSize
和 sortOrder
来控制分页、每页大小和排序。当页面变化时,handlePageChange
函数会更新 currentPage
,从而触发一个新的请求。同样地,handlePageSizeChange
和 handleSortChange
函数会更新 pageSize
和 sortOrder
。请求的结果会通过 data
属性返回,并根据请求结果更新 UI 状态。