I have problem to render data after API fetch because I have two apis and the second api call depend on first api ID - reactjs

App.tsx
export interface Hotel {
id: string;
name: string;
address1: string;
address2: string;
starRating: number;
images: string[];
longDescription: string;
}
// currently not using types but may be it should be like that
export interface Hotel {
id: string;
name: string;
address1: string;
address2: string;
starRating: number;
images: string[];
longDescription: string;
room: {
id: string;
name: string;
longDescription: string;
occupancy: {
maxAdults: number;
maxChildren: number;
maxOverall: number;
};
};
}
//
export interface Room {
id: string;
name: string;
longDescription: string;
}
const App: React.FC = () => {
// const [loading, setLoading] = useState<boolean>(false);
const [hotels, setHotels] = useState<Hotel[]>([]);
const [rooms, setRooms] = useState<Room[]>([]);
useEffect(() => {
const getData = async () => {
const { data: hotels } = await axios.get<Hotel[]>(
'https://obmng.dbm.guestline.net/api/hotels?collection-id=OBMNG'
);
const allRooms = await Promise.all(
hotels.map(async ({ id }) => {
// don't forget to provide a type for the response
const {
data: { rooms },
} = await axios.get<{ rooms: Room[] }>(
`https://obmng.dbm.guestline.net/api/roomRates/OBMNG/${id}`
);
return rooms;
})
);
console.log(hotels);
console.log(allRooms.flat());
setHotels(hotels);
setRooms(allRooms.flat());
};
getData();
}, []);
return (
<div>
<GlobalStyle />
<ImageWrapper>
<BrandTitle>Book Your Stay</BrandTitle>
<BrandSubtitle>make yourself comfortable with us</BrandSubtitle>
</ImageWrapper>
<HotelCard hotels={hotels} rooms={rooms} />-
</div>
);
};
HotelCard.tsx
interface HotelListProps {
hotels: Hotel[];
rooms: Room[];
}
const HotelCard: React.FC<HotelListProps> = ({ hotels, rooms }) => {
return (
<div>
{hotels.map((hotel) => (
<div key={hotel.id}>
<h1>{hotel.id}</h1>
// more code
</div>
))}
{rooms.map((room) => (
<div key={room.id}>
<h1>{room.name}</h1>
// more here...
</div>
))}
</div>
);
};
export default HotelCard;
In this code I get result like all hotels from first api and then I use id which I got from first api call and use that one in second api request. I stored both api response in different state(hotels, rooms). but i got different result in screen. I need to render like, first hotel1 then 3 rooms of hotel1 then again hotel2 and 3 rooms of hotel2 and rest of them same, but rightnow I got first hotel then all rooms together. how to achieve that? I am really confuse about that could anyone help me? I will really thankful for that?

First organize your data into the structure you want
const joined = hotels.map(h => {
hotel: h,
rooms: rooms.filter(r => r.hotelId === h.id), // Or however your docs are linked
});
// Then
joined.map(j => <>
<h1>{j.hotel.id}</h1>
{j.rooms.slice(0, 2).map(r => <h1>{r.id}</h1>)}
</>);

Related

Get Element Of Array By Index in Typescript+React

How can I get an element by index in Typescript+React? I keep getting the error: Element implicitly has an 'any' type because expression of type '0' can't be used to index type '{ code: string; name: string; dateTime: string; }'. Maybe it's a stupid, but I don't have much experience with Typescript, and I would like departure[0] and then departure[1] to be displayed separately on the page
const [result, setResult] = useState<ConversionData[]>([]);
type ConversionData = {
uuid: string;
airlineCode: string;
price: {
amount: number;
currency: string;
};
bounds: Array<{
departure: {
code: string;
name: string;
dateTime: string;
};
destination: {
code: string;
name: string;
dateTime: string;
};
duration: string;
}>;
};
useEffect(() => {
const api = async () => {
const data = await fetch("http://localhost:3001/flights").then((res) =>
res.json()
);
setResult(data);
};
api();
}, []);
return (
<div className="App">
{result.map((value) => {
return (
<>
{" "}
<div className="container">
{value?.bounds.map((newvalue) => {
const departure = newvalue.departure;
const destination = newvalue.destination;
const dates = new Date(departure.dateTime);
const dates2 = new Date(destination.dateTime);
return (
<>
<div>
<img
src={
"https://d1ufw0nild2mi8.cloudfront.net/images/airlines/V2/srp/result_desktop/" +
value.airlineCode +
".png"
}
/>
{departure.code}
<br />
do {destination.code}
<br />
Xxx:
{dates.getUTCHours()}
Xxx:
{dates2.getUTCHours()}
<br />
{dates.getUTCDate()}-
{dates.toLocaleString("en-US", { month: "short" })}-
{dates.getUTCFullYear()}
Xxx
{dates2.getUTCDate()}-
{dates2.toLocaleString("en-US", { month: "short" })}-
{dates2.getUTCFullYear()}
<div className="line" />
</div>
</>
);
})}
I searched on internet and found solutions with keyof but it doesn't work
It sounds like the data object needs to be cast as the ConversionData type. When fetch returned the data, it's unaware of what type it is.
setResult(data as ConversionData[]);
You may first need to cast it to unknown:
setResult(data as unknown as ConversionData[]);
I did a double mapp unnecessarily. I needed bounds[0].departure as in comment

Type '[Images]' is not assignable to type 'string'.ts(2322)

I'm trying to pass array of objects through props but I got that error:
(property) images: [Images]
Type '[Images]' is not assignable to type'string'.ts(2322)
ProductBlock.tsx(4, 5): The expected type comes from this index signature.
Here is my block:
interface Images {
[key: string]: string;
}
interface Colors {
[key: string]: string;
}
interface Product {
_id: string;
name: string;
description: string;
price: number;
colors: [string];
images: [Images];
type: string;
likes: number;
}
const ProductBlock = (product: Product) => {
console.log(product.images); // [{...}]
const RenderColors = () => {
}
const RenderImages: React.FC<Images> = (images: Images) => {
console.log(images);
return(
<>
</>
)
}
return (
<div className="product product-container" key={product._id}>
<RenderImages images={product.images}/> //<--- Error here at "images="
<h3 className="product-title">{product.name}</h3>
</div>
)
}
export default ProductBlock;
product.images is an array of Images objects, but you are passing it to RengerImages which is taking a single Images object as the parameter.
Try this:
const ProductBlock = (product: Product) => {
console.log(product.images); // [{...}]
const RenderColors = () => {
}
const RenderImages: React.FC<{ images: [Images] }> = ({ images }) => {
console.log(images);
return(
<>
</>
)
}
return (
<div className="product product-container" key={product._id}>
<RenderImages images={product.images}/>
<h3 className="product-title">{product.name}</h3>
</div>
)
}

How to define typescript interface for array on objects

Here is the data from the server.
I want to display it with map on UI.
Here is the interface I did -
export interface IHistory {
reports: Readonly<{
readonly id?: string,
status?: Status,
readonly created_at?: Date,
}>
}[];
The map I'm doing:
{props.history!.map((index, idx) => {}
The error:
TypeError: Cannot read properties of null (reading 'map')
What am I doing wrong?
I want to display only the reports.
Added -
Interface -
export interface IHistory {
reports: Array<{
id?: string;
status?: Status;
created_at?: string;
}>;
};
const [ hitoryState, setHistoryState ] = useState<IHistory | null>(null);
useEffect(() => {
backendAPIAxios.get('/history')
.then((response: AxiosResponse<IHistoryResponse>) => {
if (!response.data) {
return alert('Failed to get history');
}
setHistoryState(() => response.data);
})
.catch((e: AxiosError) => {
// alert(`Failed to get history with error: ${e}`);
});
}, [setHistoryState])
console.log(props.history!.reports.map((hist) => <p>{hist.created_at}</p>))
This is the error I'm getting:
You are making IHistory an array of reports objects, when reports is the field with the array. Also, created_at will likely be a string and not a date if it's being returned from the backend.
type Status = "fair" | "unfair";
interface IHistory {
reports: Array<{
id?: string;
status?: Status;
created_at?: string;
}>;
};
const backendHistory: IHistory = {
reports: [
{ id: "123", status: "fair", created_at: new Date().toISOString() },
{ id: "456", status: "unfair", created_at: new Date().toISOString() },
]
};
const result = backendHistory.reports.map(({ id }, _idx) => id);
console.log("result", result);
React code:
import React from "react";
type Status = "fair" | "unfair";
interface IHistory {
reports: Array<{
id?: string;
status?: Status;
created_at?: string;
}>;
}
async function fakeFetch(): Promise<IHistory> {
const backendHistory: IHistory = {
reports: [
{ id: "123", status: "fair", created_at: new Date().toISOString() },
{ id: "456", status: "unfair", created_at: new Date().toISOString() }
]
};
return new Promise((resolve) =>
setTimeout(() => resolve(backendHistory), 1000)
);
}
export default function App() {
const [backendHistory, setBackendHistory] = React.useState<IHistory>();
React.useEffect(() => {
let isAlive = true;
(async function () {
const result = await fakeFetch();
if (isAlive) {
setBackendHistory(result);
}
})();
return () => {
isAlive = false;
};
}, []);
return (
<div className="App">
<h1>Backend History</h1>
{backendHistory ? (
backendHistory.reports.map((hist) => <p>{hist.id}</p>)
) : (
<span>loading</span>
)}
</div>
);
}

Type '{ userId: string; }' has no properties in common with type 'AxiosRequestConfig'. | Axios - Next.js with typescript

( Hi comunnity ) I have this piece of code, everything was working fine, but got an error once i create the API.delete function, don't know what is going on there actually
import axios, { AxiosRequestConfig } from "axios";
const API = axios.create({ baseURL: "http://localhost:5000/api" });
// Set header for each request to give permission
API.interceptors.request.use((req: AxiosRequestConfig) => {
if (localStorage.getItem("Auth")) {
req.headers.Authorization = `Bearer ${
JSON.parse(localStorage.getItem("Auth")).token
}`;
}
return req;
});
// login - register - update perfil
export const login = (loginData: {
email: string | null;
password: string | null;
}) => API.post(`/user/login`, loginData);
export const register = (registerData: {
email: string;
password: string;
name: string;
}) => API.post("/user/register", registerData);
export const updatePerfilPhotos = (
photosBase64: { perfil?: string; banner?: string },
id: string
) => API.patch(`/user/update/${id}`, photosBase64);
export const AddNotification = (
userInformation: { userId: string },
id: string
) => API.patch(`/user/notification/${id}`, userInformation);
export const DeleteNotificationOrFriend = (
userInformation: { userId: string },
id: string
) => API.delete(`/user/deleteNotification/${id}`, userInformation);
//
In the API.delete function there's a problem :
(parameter) userInformation: {
userId: string;
}
Type '{ userId: string; }' has no properties in common with type 'AxiosRequestConfig'
What does that mean ? why is that happening, how can i fix this ?
Thanks for your time friends !
I think the delete method signature should be like this,
API.delete(`/user/deleteNotification/${id}`, { data: userInformation })
Refer: https://github.com/axios/axios/issues/897#issuecomment-343715381

React testing with Typescript: Passing array of objects as props

I am trying to pass an array of objects to mock component data for testing like so:
const mockPackage = {
id: '1232-1234-12321-12321',
name: 'Mock Package',
price: 8.32,
description: 'Mock description',
globalProduct: {
imageUrl: 'http://imageurl.com',
},
};
const mockPackageData = {
name: 'Package',
packages: [mockPackage],
};
beforeEach(() => {
component = render(
<SuiteContextProvider>
<PackageCard
showDetail={{ display: true, selectedIndex: 1, hideOthers: false }}
handleShowDetail={handleShowDetail}
packageData={mockPackageData}
/>
</SuiteContextProvider>,
);
});
However, I receive the following error:
The component that receives the data destructures the packageData like so:
export interface Package {
id: string;
name: string;
price: number;
description: string;
globalProduct: {
imageUrl: string;
};
}
export interface PackageData {
name: string;
packages: [];
}
type Props = {
packageData: PackageData;
handleShowDetail: (data: DefaultPackageProps) => void;
showDetail: {
display: boolean;
selectedIndex: number | null;
hideOthers: boolean;
};
};
const PackageCard: React.FC<Props> = ({ packageData, handleShowDetail, showDetail }: Props) => {
return (
<>
{packageData.packages.map((packageInfo: Package, index: number) => {
const {
id,
name,
price,
description,
globalProduct: { imageUrl },
} = packageInfo;
Your PackageData defintion should be
export interface PackageData {
name: string;
packages: Package[];
}
You current code packages: []; declares packages to must be an empty array that is why you get the type '0' error.

Resources