这是关于React Querywith TypeScript的系列文章中的第二篇。在上一篇文章中,我们使用useQuery 钩子做了一个基本的网络服务请求。这篇文章将扩展这个例子,做第二个请求,需要第一个请求中的数据:
我们的需求
目前,我们的React组件请求星球大战API中的people 资源并显示角色的名字。
这就是此刻的组件:
export default function App() {
const { status, error, data } = useQuery<Character, Error>(
["character", { id: 1 }],
getCharacter
);
if (status === "loading") {
return <div>...</div>;
}
if (status === "error") {
return <div>{error!.message}</div>;
}
return data ? <h3>{data.name}</h3> : null;
}
响应中还有一个homeworld 字段,它是一个URL,可以获得角色的母星信息:
{
"name": "Luke Skywalker",
...
"homeworld": "https://swapi.dev/api/planets/1/", ...
}
我们需要向这个URL发出请求,并在角色的名字旁边显示其母星的名字。
第二个获取函数
我们将开始创建获取母星的取值函数:
async function getPlanet({
queryKey,
}: {
queryKey: [string, { url: string }];
}) {
const [, { url }] = queryKey;
const response = await fetch(url);
if (!response.ok) {
throw new Error("Problem fetching planet");
}
const data = await response.json();
assertIsPlanet(data);
return data;
}
查询键被传递到该函数中,它是一个元组,其中第二个元素包含应被用来进行请求的URL。
该函数发出一个简单的请求,如果不成功就会引发一个错误。
数据的类型用assertIsPlanet type assert函数断定为Planet :
type Planet = {
name: string;
};
function assertIsPlanet(data: any): asserts data is Planet {
if (!("name" in data)) {
throw new Error("Not home planet");
}
}
第二个查询
现在让我们使用useQuery 钩子在组件中添加第二个查询:
import { useQuery } from 'react-query'
export function App() {
const {
status: characterStatus,
error: characterError,
data: characterData,
} = useQuery<Character, Error>(["character", { id: 1 }], getCharacter);
const { status: planetStatus, error: planetError, data: planetData, } = useQuery<Planet, Error>(["planet", { url: characterData?.homeworld }], getPlanet); ...
}
我们对状态变量进行了别名,这样它们就不会发生冲突。
我们还将字符响应数据中的homeworld 字段传递给了getPlanet 取值函数。
现在让我们进行渲染:
export default function App() {
...
if (characterStatus === "loading" || planetStatus === "loading") {
return <div>...</div>;
}
if (characterStatus === "error") {
return <div>{characterError!.message}</div>;
}
if (planetStatus === "error") {
return <div>{planetError!.message}</div>;
}
return (
<div>
{characterData && <h3>{characterData.name}</h3>}
{planetData && <p>{planetData.name}</p>}
</div>
);
}
该组件编译正常,这很好,正确的字符和星球名称会被渲染出来:

然而,如果我们在DevTools的网络面板上看一下,我们会发现在收到字符响应之前,已经发出了一个行星请求。因此,向一个undefined URL发出了请求:
当收到一个字符响应时,发生了重新渲染,第二次调用getPlanet ,并提出了一个有效的请求。
当第一个查询返回后执行第二个查询
虽然正确的数据被呈现出来,但我们想停止对getPlanet 的第一次调用。
我们用useQuery 中的enabled 选项来解决这个问题。这被设置为一个布尔表达式。当它被设置后,查询将只在它被评估为true 时运行。
我们可以使用enabled 选项,将其设置为字符数据中的homeworld 属性是否有一个值:
const {
status: planetStatus,
error: planetError,
data: planetData,
} = useQuery<Planet, Error>(
["planet", { url: characterData?.homeworld }],
getPlanet,
{ enabled: !!characterData?.homeworld, });
现在我们只得到一个关于星球数据的请求。
很好 😊
这篇文章中的代码可以在CodeSandbox中找到:codesandbox.io/s/dependent…