reactjs - 如何使变量具有未知类型或分配“电影”?
问题描述
我正在开发一个钩子,我可以在其中传递一个发出 Web 请求并返回的函数isLoading
,data
和error
.
const [isLoading, data, error] = useApi(getMovie, idMovie, someAction);
基本上我有一个useApi
接收 3 个参数的钩子 ():
- 解析网络请求的函数
- 此 Web 请求的参数
- 最后是对回调的调用。
我这样使用它:
const idMovie = { _id: "3" };
// callback function
const someAction = (data: unknown) => {
// do something
return data;
};
const [isLoading, data, error] = useApi(getMovie, idMovie, someAction);
使用Api.tsx
import { AxiosPromise, AxiosResponse } from 'axios';
import { useState, useEffect } from 'react';
const useApi = (
apiFunction: (params: unknown) => AxiosPromise,
params = {},
callback: (data: unknown) => void
): [boolean, unknown, null | string] => {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<null | string>(null);
useEffect(() => {
apiFunction(params)
.then(({ data }: AxiosResponse) => {
setData(data);
setIsLoading(false);
if (callback) {
callback(data);
}
})
.catch(() => {
setError('Something went wrong');
setIsLoading(false);
});
}, []); //apiFunction, params, callback]
return [isLoading, data, error];
};
export default useApi;
getMovie
对应于解决网络请求的函数
import axios from "axios";
type getMovieParams = { _id: string };
const BASE_URL = "https://jsonplaceholder.typicode.com/todos/";
const getMovie = (params: getMovieParams): Promise<unknown> => {
if (params) {
const { _id } = params;
if (_id) {
const url = `${BASE_URL}/${_id}`;
return axios.get(url);
}
}
throw new Error("Must provide a query");
};
export default getMovie;
调用此钩子的代码如下所示:
import "./styles.css";
import useApi from "./useApi";
import getMovie from "./api";
interface Movie {
userId: number;
id: number;
title: string;
completed: boolean;
}
export default function App() {
const idMovie = { _id: "3" };
// callback function
const someAction = (data: unknown) => {
// do something
return data;
};
const [isLoading, data, error] = useApi(getMovie, idMovie, someAction);
const dataResponse = error ? [] : data; //type Movie
console.log(dataResponse);
if (isLoading) {
return <div>Loading...</div>;
}
return (
<div>
{error && <div>{error}</div>}
-- Get Data Movie 1--
<p>{dataResponse.title}</p>
</div>
);
}
我在这一行遇到打字错误:
<p>{dataResponse.title}</p>
这是因为:
const dataResponse = error ? [] : data;
data
是类型unknown
,这是因为它可以是任何类型data
,但我想在这种情况下指定数据类型是' Movie
',想法是在另一个组件中重用这个钩子并能够说出数据的类型将在此钩子返回时获得data
。
这是我的实时代码:
https://codesandbox.io/s/vigilant-swartz-iozn4
创建此问题的原因是寻求您的帮助以解决标有红线的打字稿问题。(文件 app.tsx)
如何解决?谢谢
解决方案
可以使用泛型来捕获与您传递给函数的参数相关的类型。例如,在你的截图中,TS 对参数的抱怨,我们可以声明一个泛型来捕获参数类型
function useApi<Params>(
apiFunction: (params: Params) => AxiosPromise,
params: Params,
callback: (data: unknown) => void
)
这样,我们的第二个钩子参数将被键入apiFunction
为其 arg 中要求的任何类型。
我们做同样的事情来输入我们返回给回调的数据
function useApi<Params, Return>(
apiFunction: (params: Params) => Promise<AxiosResponse<Return>>,
params: Params,
callback: (data: Return) => void
)
apiFunction
我通过检查axios.get
调用的类型得到了返回类型
get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
如您所见,最后它返回一个Promise<AxiosResponse<T>>
. 我们用我们的通用类型截取它T
并命名它Return
现在我们也可以在钩子的返回类型和主体中使用这些类型
): [boolean, Return | null, null | string] {
const [data, setData] = useState<Return | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<null | string>(null);
useEffect(() => {
apiFunction(params)
.then(({ data }) => {
setData(data);
setIsLoading(false);
if (callback) {
callback(data);
}
})
.catch(() => {
setError("Something went wrong");
setIsLoading(false);
});
}, []); //apiFunction, params, callback]
return [isLoading, data, error];
}
由于您的 Movie 模型是特定于 getMovie 调用的,因此您可以将该界面移到那里并键入axios.get
调用
interface Movie {
userId: number;
id: number;
title: string;
completed: boolean;
}
const getMovie = (params: getMovieParams) => {
if (params) {
const { _id } = params;
if (_id) {
const url = `${BASE_URL}/${_id}`;
return axios.get<Movie>(url);
}
}
throw new Error("Must provide a query");
};
通过所有这些添加,当您使用钩子时,您会在代码上观察到一些警告,这将迫使您在考虑所有可能值的情况下实现渲染,我像这样重写它
...
const [isLoading, dataResponse, error] = useApi(
getMovie,
idMovie,
someAction
);
if (isLoading) {
return <div>Loading...</div>;
}
if (error) return <div>error</div>;
return <div>{dataResponse?.title}</div>;
...
推荐阅读
- visual-studio-code - 有没有办法将 VSCode 配置为始终在新窗口中打开项目?
- python - 如何在python 3中生成随机浮点数
- oracle-apex - 如何通过使用 BI Publisher 在 APEX 中传递参数来打印单个查询
- javascript - highlight.js 的自定义语言定义
- c - 使用 sem_t 时大小为 8 的无效写入
- angular - 如何在 ftl 文件中获取当前页面 url
- javascript - Angular 模板未在 RxJS 可观察更改上更新
- api - 无法连接到服务器端 API
- android - Android Studio 4.2 在我尝试打开它时没有响应
- python - 为什么在这个例子中没有提出未定义的 NameError?