#reactjs #typescript #api
#reactjs #typescript #API
Вопрос:
Я нашел онлайн-пример пользовательского хука службы api с typescript, который, как мне показалось, имеет смысл, поэтому я попытался применить его к своим потребностям. Происходит пара вещей, которые для меня не имеют смысла. Во-первых, когда я выхожу из системы, данные, возвращающиеся из api, регистрируются дважды, чего я не понимаю.
Во-вторых, когда я выхожу из service.payload.results, ‘results’ не определен, но если я регистрирую только service.payload, он регистрирует массив, как и ожидалось (за исключением дважды). Я пытаюсь ввести результаты моего вызова api и применить форму к данным. Я не могу понять, что это такое. Вот мой код. Пожалуйста, посмотрите, можете ли вы заметить что-нибудь, чего я не понимаю или явно не понимаю.
- Типы доменов (Visitor.ts):
export interface Visit {
id: string;
visited: string;
}
export interface Visitor {
id: string;
name: string;
visits: Array<Visit>;
}
- Типы служб (Service.ts):
interface ServiceInit {
status: "init";
}
interface ServiceLoading {
status: "loading";
}
interface ServiceLoaded<T> {
status: "loaded";
payload: T;
}
interface ServiceError {
status: "error";
error: Error;
}
export type Service<T> =
| ServiceInit
| ServiceLoading
| ServiceLoaded<T>
| ServiceError;
- Пользовательский хук:
import { useEffect, useState } from "react";
import Axios from "axios";
import { Service } from "../types/Service";
import { Visitor } from "../types/Visitor";
export interface Visitors {
results: Visitor[];
}
const useVisitorService = () => {
const [result, setResult] = useState<Service<Visitors>>({
status: "loading",
});
const url: string = "api/visitors";
const config = {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
};
useEffect(() => {
const requestData = async function () {
try {
const res = await Axios.get(url, config);
setResult({ status: "loaded", payload: res.data });
}
catch (error) {
setResult({ status: "error", error });
}
};
requestData();
}, []);
return resu<
};
export default useVisitorService;
- Компонент, использующий перехват (VisitList.tsx) :
import React from "react";
import useVisitorService from "../services/useVisitorService";
const VisitList: React.FC<{}> = () => {
const service = useVisitorService();
if (service.status === "loaded") {
// this logs the expected array of objects from server (twice)
console.log(service.payload);
}
//this errors because results is undefined
return (
<div>
{service.status === "loading" amp;amp; <div>Loading...</div>}
{service.status === "loaded" amp;amp;
service.payload.results.map((visitor) => (
<div key={visitor.id}>{visitor.name}</div>
))}
{service.status === "error" amp;amp; (
<div>Error.</div>
)}
</div>
);
};
export default VisitList;
Комментарии:
1. Рискуя быть спамом, вам может понравиться react-ketting . У него есть несколько хороших хуков, и он также отменяет дублирование запросов GET, даже если они исходят от несвязанных компонентов react.
2. Я посмотрю на это, чтобы увидеть, имеет ли это значение для меня. Спасибо.
Ответ №1:
Это сложная задача, и я не совсем уверен, что происходит. Можете ли вы настроить изолированную среду, в которой есть полный URL для извлечения?
Потенциальная проблема, которую я вижу, может быть или не быть проблемой, в зависимости от того res
, как выглядит возвращаемый объект. Я подозреваю, что вы устанавливаете service.payload
вместо service.payload.results
, но я могу ошибаться.
Через ваши типы вы сказали, что успешный результат выглядит следующим образом:
interface ServiceLoaded {
status: "loaded";
payload: {
results: Visitor[];
}
}
где полезная нагрузка должна иметь свойство results
, представляющее собой массив Visitor
объектов.
Это явно не указано или не проверено в вашем коде, что делает его подверженным ошибкам. Вы устанавливаете payload
res.data
значение, которое может быть любого типа.
setResult({ status: "loaded", payload: res.data });
res.data
Содержит ли свойство results
, в котором перечислены посетители? Если он просто возвращает список посетителей, вам нужно присвоить его results
свойству самостоятельно, и это будет источником ваших проблем. (Или вам нужно изменить тип / интерфейс посетителей, чтобы это был просто посетитель массива [], а не объект, содержащий массив.)
setResult({ status: "loaded", payload: results: { res.data } });
Не зная, как вы res.data
на самом деле выглядите, я могу предложить вам использовать защиту типа typescript для его проверки, поскольку в res.data
настоящее время Axios имеет тип any
. Таким образом, вы знаете, что payload.results
это всегда будет определено, если service.status === "loaded"
. Если Axios возвращает неожиданный результат, setResult
следует сохранить ошибку.
Ваша защита типа выглядит примерно так. Я просто проверяю, что свойство results
существует, но вы могли бы получить более подробную информацию и проверить, что это массив и что каждый элемент является Vistor .
const isValidPayload = (payload: any): payload is Visitors => {
return typeof payload === "object" amp;amp; "results" in payload;
}
Ваша requestData
функция теперь выглядит примерно так, где мы все еще улавливаем ошибки, возвращаемые из Axios, но также улавливаем ошибки из-за неверных ответов.
const requestData = async function () {
try {
const res = await Axios.get(url, config);
if ( isValidPayload(res.data) ) {
setResult({ status: "loaded", payload: res.data});
} else {
setResult({ status: "error", error: {
name: "Invalid Format",
message: "API results must contain an array of Visitor objects",
}});
}
}
catch (error) {
setResult({ status: "error", error });
}
};