我有一个页面,调用一个API与特定的ID来获取ID的信息。在同一个页面上,我调用了一个不同的API随机生成id,这样它就可以向用户推荐产品。
我发现调用随机ID的API与页面相比延迟很大。有没有人知道我如何才能使这一部分的网页加载一样快的其他网页?
显示两个API调用的页面:
import React from 'react'
import { Link, useParams } from 'react-router-dom'
import { useState, useContext } from 'react'
import GetSingleProduct from '../Hooks/getSingleProduct'
import { AppContext } from '../Context/Context'
import '../Assets/Styles/Product.css'
import FooterIcons from '../Components/Banners/FooterIcons'
import Recommended from '../Components/Recommended'
function Product() {
// able to use useParams to direct to the page with the id of the product that has been clicked
const { id } = useParams()
const { singleProduct, isLoading, error } = GetSingleProduct(`https://makeup-api.herokuapp.com/api/v1/products/${id}.json`)
const [clickedColour, setClickedColour] = useState([])
// const disablePlusBtn = () => {
// singleProduct.quantity === 10 || singleProduct.quantity === 0 ?
// <button className='cursor-not-allowed'>+</button> && setDisableBtn(true)
// : <button onClick={handleIncrement}>+</button>
// }
// useContext for the add to cart
const CartState = useContext(AppContext);
const dispatch = CartState.dispatch;
const handleColour = (index) => {
setClickedColour(prevstate =>
({
...prevstate, [index] // copies prev state
: !prevstate[index]
}))
console.log(setClickedColour)
}
return (
<>
<div className='routeTaken'>
<ol>
<li><Link to='/'>Home</Link></li>
<li><Link to='/shop'>Shop</Link></li>
<li>Current page</li>
</ol>
</div>
<div className='productInfo'>
{error && <div>{error}</div>}
{isLoading ?
(<div>Loading...</div>) :
<div id={singleProduct.id}>
<img src={singleProduct.api_featured_image} alt={singleProduct.product_type}></img>
<p>{singleProduct?.product_type ? singleProduct.product_type.charAt(0).toUpperCase() + singleProduct.product_type.slice(1).toLowerCase().split('_').join(' ') : singleProduct.product_type}</p>
<h1>{singleProduct?.brand ? singleProduct.brand.charAt(0).toUpperCase() + singleProduct.brand.slice(1).toLowerCase() : singleProduct.brand} {singleProduct?.name ? singleProduct.name.charAt(0).toUpperCase() + singleProduct.name.slice(1).toLowerCase() : singleProduct.name}</h1>
<h2> £{singleProduct.price === '0.0' || singleProduct.price === null ? '8.50' : Number(singleProduct.price).toFixed(2)}</h2>
<h3>{singleProduct.description}</h3>
{/* {singleProduct.map(colour =>
<h1>{colour.product_colors}</h1>)} */}
<div className="colourList grid grid-cols-4">
{singleProduct.product_colors?.map((colour, index) => {
return (
<button onClick={() => handleColour(index)} className='colourItem' key={index} style={{ backgroundColor: colour.hex_value }}>
{colour.hex_value}
{clickedColour[index] ?
(<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5" />
</svg>
) : (null)}
</button>
)
})}
</div>
<button onClick={() => dispatch({ type: 'ADD', payload: singleProduct })}>Add to basket</button>
</div>
}
</div >
<div>
<FooterIcons />
</div>
<div>
<Recommended />
</div>
</>
)
}
export default Product
字符串
我使用自定义钩子来调用API。
这就是运行良好的API
getSingleProduct.js
import { useEffect, useState } from "react"
import axios from 'axios'
const GetSingleProduct = (url) => {
const [isLoading, setIsLoading] = useState(false)
// output error code
const [error, setError] = useState(false)
const [singleProduct, setSingleProduct] = useState([])
useEffect(() => {
const singleAPI = async (url) => {
// useCallback means that the API call will not be made everytime we make a change to the page e.g. reviewing a products info
// useCallback means that the API call will only be called when it needs to be e.g. on page refresh. we use useCallback instead of useMemo as we want the function to be returned and not just the value
setIsLoading(true)
try {
const res = await axios.get(url)
if (res.status === 200) {
console.log('Success!');
const productWithQuantity = {...res.data, quantity:1}
setSingleProduct(productWithQuantity);
}
else {
console.log(`Server error: ${res.status}`);
}
} catch (err) {
console.log(`Fetch error: ${err}`)
setError(err.message);
;
}
finally {
setIsLoading(false)
}
}
singleAPI(url)
}, [url]);
return {
isLoading,
error,
singleProduct
}
}
export default GetSingleProduct
型
这个比较慢。
import { useEffect, useState } from "react"
import axios from 'axios'
const GetRecommended = (url) => {
const [recommended, setRecommended] = useState([])
useEffect(() => {
const getRecommendedProducts = async (url) => {
// useCallback means that the API call will not be made everytime we make a change to the page e.g. reviewing a products info
// useCallback means that the API call will only be called when it needs to be e.g. on page refresh. we use useCallback instead of useMemo as we want the function to be returned and not just the value
try {
const res = await axios.get(url)
if (res.status === 200) {
console.log('Success!');
// const randomIndex = Math.floor(Math.random() * res.data.length)
// console.log(randomIndex)
// empty array to push the random numbers to
// adds 4 random numbers to the empty array
const listRecommended =[]
for(let i=0; i<4; i++){
listRecommended.push(Math.floor(Math.random() * res.data.length))
}
console.log(listRecommended)
// filter through res.data and filter out the ids that match with listRecommended
const filterOut = res.data.filter(({id})=> listRecommended.includes(id))
console.log(filterOut)
// converts prices that are set to 0.0 by the API and adds quantity of 1 to eahc object in the data array
const productsWithQuantity = filterOut.map((item) => {
return (
item.price === '0.0' || item.price === null ? { ...item, price: 8.50, quantity: 1 } : { ...item, quantity: 1 }
)
})
setRecommended(productsWithQuantity);
}
else {
console.log(`Server error: ${res.status}`);
}
} catch (err) {
console.log(`Fetch error: ${err}`);
}
}
getRecommendedProducts(url)
}
, [url]);
return {
recommended,
}
}
export default GetRecommended
型
Recommended.js(产品页面上的组件)
import React from 'react'
import GetRecommended from '../Hooks/getRecommended'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBasketShopping } from '@fortawesome/free-solid-svg-icons';
import { Link } from 'react-router-dom'
import { useContext } from 'react'
import { AppContext } from '../Context/Context'
import savedHook from '../Hooks/savedHook'
import'../Assets/Styles/Shop.css'
function Recommended() {
const { recommended } = GetRecommended('https://makeup-api.herokuapp.com/api/v1/products.json')
const { likedIndex, changeIcon } = savedHook()
const listRecommended = []
for(let i=0; i<4; i++){
listRecommended.push(Math.floor(Math.random() * recommended.length))
}
const filterOut =recommended.filter(({id})=> listRecommended.includes(id))
console.log(filterOut)
// filter out the ids that match with listRecommended
return (
<>
<h1 className='recommendedHeader text-2xl'>Recommended</h1>
<div className='recommended grid grid-cols-2 py-4 md:grid-cols-4 lg:grid-cols-4'>
{filterOut.map((item, index) =>{
return(
<div className='topProductCard h-full flex flex-col mx-1 transition ease-in-out delay-150 hover:-translate-y-1 hover:scale-100 hover: duration-300 hover:shadow-lg ' key={item.id}>
< button className='mx-1 my-1' value={item.brand + item.product_type} onClick={() => { changeIcon(index); saveDispatch({ type: 'SAVE', saveIt: item }) }}>
{likedIndex[index] ?
(
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="redHeart w-6 h-6">
<path d="M11.645 20.91l-.007-.003-.022-.012a15.247 15.247 0 01-.383-.218 25.18 25.18 0 01-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0112 5.052 5.5 5.5 0 0116.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 01-4.244 3.17 15.247 15.247 0 01-.383.219l-.022.012-.007.004-.003.001a.752.752 0 01-.704 0l-.003-.001z" />
</svg>
)
: (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="w-6 h-6" >
<path strokeLinecap="round" strokeLinejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" />
</svg>
)
}
</button>
<Link to={`/product/${item.id}`} name={item.brand}>
<img className='topPickImage ml-8 mb-2 md:ml-12 xl:ml-20' src={item.api_featured_image} alt={item.brand + item.product_type}></img>
{/* To display the brand name with as sentence case */}
<div className='topPickText px-2 flex flex-col content-end'>
<p className='topPickProductType'>{item.product_type ? item.product_type.charAt(0) + item.product_type.slice(1).toLowerCase().split('_').join(' ') : item.product_type}</p>
<p className='topPickBrand'>
{item?.brand ? item.brand.charAt(0).toUpperCase() + item.brand.slice(1).toLowerCase() : item.brand} </p>
<p className='topPickName'>{item?.name ? item.name.charAt(0).toUpperCase() + item.name.slice(1).toLowerCase() : item.name}</p>
</div>
</Link>
<div className='flex flex-row mt-auto mx-2 my-2'>
<div className='mr-auto mt-auto'>
<p className='topPickNumber'><span className='circleShadow '>£{Number(item.price).toFixed(2)}</span>
</p>
</div>
<div className='addToCart ml-auto mt-auto '>
<button className='basketTopPicks ' onClick={() => dispatch({ type: 'ADD', payload: item })}>
<FontAwesomeIcon icon={faBasketShopping} />
</button>
</div>
</div>
</div>
)
}
)}
</div >
</>
)
}
export default Recommended
型
我不确定是因为它是Product.js页面中的一个组件还是因为Recommended.js中的随机数和排序而慢?
1条答案
按热度按时间rks48beu1#
假设您的推荐API相当快,您可以做的另一个改进是,在GetSingleProduct.isLoading为true之前呈现组件。
是从
字符串
至
型
这样,你推荐的API将触发器与getProduct api并行。
也尝试检查网络时间为这些api在网络选项卡或使用控制台。time / timeEnd。