reactjs 由于请求太多,我的react应用程序无法呈现数据(错误429)

iszxjhcz  于 2022-12-12  发布在  React
关注(0)|答案(1)|浏览(170)

我正在使用RTK查询从rapid api获取数据,有时数据被渲染,有时不被渲染。在数据不被渲染的情况下,当我检查控制台时,我注意到有太多的请求(状态代码429)错误.我实际上只使用过一次RTK查询,但是因为有一堆东西需要更新,例如当我提交一个表单时,重新渲染包括RTK钩子来发出请求,这可能是导致太多请求错误的原因。我的问题是:

  • 如果在一次更新中发生了几次重新渲染,第一次重新渲染会导致RTK挂钩发出请求,假设这之后立即进行第二次重新渲染,而RTK中没有参数更改,这意味着RTK使用缓存中存储的数据,没有向服务器发送请求,那么为什么会出现“请求过多”错误。
  • 是否有任何方法可以强制执行RTK挂钩,只要有一个依赖关系的变化,就像我们在useEffect中使用的那样?
    UPDATE以下是我的代码:

App.js

import {createContext} from 'react'
import './App.css';
import {Provider} from 'react-redux';
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom'
import SearchComponent from './components/SearchComponent'
import NavBarComponent from './components/NavBarComponent'
import {useGetSearchLocationQuery, useGetCurrentWeatherApiQuery} from './redux/services/weatherApi'
import {useGetCityQuery} from './redux/services/countryApi'
import {setCity} from './redux/slices/locationSlice'
import {useSelector} from 'react-redux'
import Today from './pages/Today'
import Home from './pages/Home'
import Week from './pages/Week'


export const Context = createContext();

const App=()=> {

const {city} = useSelector(state => state.location )

//fetch data on first load page to get user's geolocation
const {data: fetchedCity, isLoading,  isError, isFetching} = useGetCityQuery()
//fetch city's id
const {data: locationData, isLoading: isLoadingId, isError: isErrorId} = useGetSearchLocationQuery( city || fetchedCity?.name )

if(isLoading || isLoadingId) return <p>data loading from App</p>
if(!locationData) return <p>No location data from App</p>

  return (
    <Context.Provider  value={{fetchedCity,locationData}} >
    <div className="App">
        <NavBarComponent />
       {/*contains both sideBar and the main page (today and week pages)*/}
        <div className='pageContainer'>
            <SearchComponent/>

           {/*the container of today and weekpages*/}
            <div className='mainSectionContainer'>
                <Routes>
                    <Route path='/' element={<Home />} />
                    <Route path= '/today/:city' element={<Today /> }/>
                    <Route path= '/week/:city' element={<Week /> }/>
                </Routes>
            </div>
        </div>

    </div>
    </Context.Provider>

  );
}

export default App;

SearchComponent.js:此组件有时也不加载数据

import React, {useState, useContext, useEffect} from 'react'
import api from '../assets/api'
import '../style/searchBarStyle.css'
import {GoSearch} from 'react-icons/go'
import {TiDelete} from 'react-icons/ti'
import { IconContext } from "react-icons";
import {weatherIconsArray} from '../assets/api'
import {useDispatch, useSelector} from 'react-redux'
import {setCity, setClickedCardIndex} from '../redux/slices/locationSlice'
import {useGetSearchLocationQuery, useGetCurrentWeatherApiQuery} from '../redux/services/weatherApi'
import {Context} from '../App';
import {useNavigate} from 'react-router-dom'

const SearchComponent = () =>{

const navigate = useNavigate();
const dispatch = useDispatch()
const [searchInput, setSearchInput] = useState('')
const {city} = useSelector(state =>state.location)

//locationData contains the city's id property
const {locationData, fetchedCity} = useContext(Context)

//fetch the current weather details based on the city's id
const {data: currentWeatherData, isLoading: isCurrentLoading, isError: isErrorCurrent} = useGetCurrentWeatherApiQuery(locationData?.locations[0]?.id)

//return an object of an image and a weather description to render in the DOM
const {image, description} = weatherIconsArray?.find(item => currentWeatherData?.current?.symbol === item.symbol) || {}

useEffect(()=>{

if(city === fetchedCity?.name)
setSearchInput('')

},[city])

const handleSubmit=(e) =>{
e.preventDefault();
if(!searchInput){
alert("Please set a location in the search field")
return
}

//update the city state
dispatch(setCity(searchInput))
navigate(`/today/${searchInput}`)
}

if(isCurrentLoading) return <p>is loading from searchbar</p>
if(!currentWeatherData) return <p>No current forecast available</p>

return(

    <div className='sidebarContainer'>
        <form onSubmit={handleSubmit}>
            <div className="searchContainer">
              <GoSearch className='icon'/>
              <input
              value={searchInput}
              onChange={(e) =>{setSearchInput(e.target.value)}}
              placeholder='search'/>
              <IconContext.Provider value={{color: 'black'}}>
              <TiDelete
              onClick={()=>{setSearchInput('')}}
              className='deleteIcon'/>
              </IconContext.Provider>
            </div>
        </form>

        <div className='weatherImg'>
              <img src={image}/>
        </div>

        <div className='temperature'>
              <p>{currentWeatherData?.current?.temperature}°C</p>
              <p>{currentWeatherData?.time}</p>
        </div>

        <hr className='separator'/>

        <div className='weatherState'>
            <p>{description}</p>
            <p>{ `Wind speed: ${currentWeatherData?.current?.windSpeed} m/s`}</p>
        </div>

        <div className='countryAndCity'>
             <p> {`${locationData?.locations[0]?.name}, ${locationData?.locations[0]?.country}`}</p>
        </div>
    </div>

    )
}
export default SearchComponent

Today.js:通常问题就发生在这里:data未定义或weekData未定义,有时两者都未定义

import React , {useEffect, useContext} from 'react'
import { Swiper, SwiperSlide } from 'swiper/react'
import {Pagination, Navigation} from 'swiper'
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import SwiperCore, { Autoplay } from 'swiper';
import {Link, useParams} from 'react-router-dom'
import TwoHoursCard from '../components/TwoHoursCard'
import UvDetailsCard from '../components/UvDetailsCard'
import WindDetailsCard from '../components/WindDetailsCard'
import SunDetailsCard from '../components/SunDetailsCard'
import '../style/todayStyle.css'
import {useSelector, useDispatch} from 'react-redux'
import {useGetNowCastQuery, useGetWeekCastQuery} from '../redux/services/weatherApi'
import {weatherIconsArray} from '../assets/api'
import {settings} from '../assets/slideSettings'
import {setCity, setClickedCardIndex, setPreviousUvIndex} from '../redux/slices/locationSlice'
import {Context} from '../App'
const Today = () =>{

const {clickedCardIndex,previousUvIndex} = useSelector(state => state.location)

const dispatch = useDispatch()
const {city} = useParams();
useEffect(()=>{
dispatch(setCity(city))
}, [city])

useEffect(()=>{
dispatch(setClickedCardIndex(-1))
dispatch(setPreviousUvIndex(NaN))
}, [clickedCardIndex])

const {locationData} = useContext(Context)

//fetch data for the upcoming 2 hours (every 15 min)
const {data, isFetching, isLoading: isLoadingNow, isError: isErrorNow} = useGetNowCastQuery(locationData?.locations[0]?.id)

//fetch data of one week, including today's highlight
const {data: weekData, isLoading: isLoadingWeek, isError: isErrorWeek} = useGetWeekCastQuery(locationData?.locations[0]?.id)

if(isLoadingNow) return <p>is loading two hours forecast</p>
if(isLoadingWeek) return <p>is loading week</p>

return(
        <>
        <p className="headers">Upcoming 2 hours:</p>
        { data ? (

        <Swiper

                modules={[Navigation, Pagination]}
                navigation
                pagination={{clickable: true}}
                breakpoints={{
                0: {slidesPerView: 1},
                480:{
                slidesPerView: 2,
                spaceBetween: 16,
                },
                768:{
                slidesPerView: 4,
                spaceBetween: 16
                },
                1024:{
                slidesPerView: 6,
                spaceBetween: 16
                },
                1150:{
                slidesPerView: 8,
                spaceBetween: 16
                }

                }}

                >
            {
              data && data?.forecast?.map((element, index) => (
                <SwiperSlide>
                <TwoHoursCard
                        key={index}
                        description={ element.symbolPhrase || weatherIconsArray.find((obj) => obj.symbol === element?.symbol)?.description}
                        image = {weatherIconsArray.find((obj) =>obj.symbol === element?.symbol).image}
                        temperature={element.temperature}
                />
                </SwiperSlide>
                ))
            }
        </Swiper>

        ) : (<p>No estimation available for the next coming 2 hours</p>)

        }

        <div>
            <p className="headers">Today&#39;s highlight:</p>

            <div className = 'todayDetails'>
                <UvDetailsCard  weekData={weekData} index={1}  clickedCardIndex={clickedCardIndex}/>
                <WindDetailsCard weekData={weekData} index={2} clickedCardIndex={clickedCardIndex}/>
                <SunDetailsCard weekData={weekData} index={3} clickedCardIndex={clickedCardIndex}/>
                <WindDetailsCard weekData={weekData} index={4} clickedCardIndex={clickedCardIndex}/>
                <WindDetailsCard weekData={weekData} index={5} clickedCardIndex={clickedCardIndex}/>
                <SunDetailsCard weekData={weekData} index={6} clickedCardIndex={clickedCardIndex}/>
            </div>

        </div>

        </>
)

}
export default Today

我也有两个不同的API切片,因为它们之间没有关系:coutryApi获取用户的位置(城市),并根据城市从另一个API切片获取所需的数据weatherApi
countryApi.js

import {createApi, fetchBaseQuery} from 
'@reduxjs/toolkit/query/react'
    
    const cryptoNewsHeader = {
            'x-bingapis-sdk': 'true',
            'x-rapidapi-host': 'spott.p.rapidapi.com',
            'x-rapidapi-key': '6e4bb62ec0msh084a4eb0f5c4a3bp149a1ejsn039034dc54d7',
    
    };
    
    const createRequest = (url) => ({ url, headers: cryptoNewsHeader});
    
    export const countryApi = createApi({
    reducerPath: 'countryApi',
    baseQuery : fetchBaseQuery({
        baseUrl : 'https://spott.p.rapidapi.com/'
                }),
    endpoints: (builder) =>({
    getCity : builder.query({ query: ()=> createRequest('/places/ip/me') }),
    
    })
    })
    export const {useGetCityQuery} = countryApi

天气预报

import {createApi, fetchBaseQuery} from '@reduxjs/toolkit/query/react'

const cryptoNewsHeader = {
    'x-bingapis-sdk': 'true',
    'x-rapidapi-host': 'foreca-weather.p.rapidapi.com',
    'x-rapidapi-key': '6e4bb62ec0msh084a4eb0f5c4a3bp149a1ejsn039034dc54d7',
};
const createRequest = (url) => ({ url, headers: cryptoNewsHeader});

export const weatherApi = createApi({
reducerPath: 'weatherApi',
baseQuery : fetchBaseQuery({
    baseUrl : 'https://foreca-weather.p.rapidapi.com/',
            }),
endpoints: (builder) =>({
getSearchLocation : builder.query({ query: (city)=> createRequest(`/location/search/${city}`) }),

getCurrentWeatherApi : builder.query({ query:  (id) => createRequest(`/current/${id}`)}),

//this returns data of the forecast for every 15 min
getNowCast : builder.query({ query: (id) => createRequest(`/forecast/15minutely/${id}`)}),

getWeekCast: builder.query({ query:(id) => createRequest(`/forecast/daily/${id}&?alt=0&tempunit=C&windunit=MS&periods=8&dataset=full`)})

})
})
export const {useGetSearchLocationQuery,
                useGetCurrentWeatherApiQuery,
                useGetNowCastQuery,
                useGetWeekCastQuery} = weatherApi

example when I don't get data loaded on DOM
Network tab in this case

nhjlsmyf

nhjlsmyf1#

我可以找出问题所在:当我试图访问一个route页面(localhost:3000/today/cityName)时,问题是发送未定义的参数到RTK查询,由于未定义的参数没有返回数据,当出现re-render时,请求被再次发送到服务器,因此出现太多请求错误!我使用跳过参数(conditonal fetching)优化了我的代码,问题得到了解决:
在搜索组件. js中

//fetch the current weather details based on the city's id
const {
  data: currentWeatherData,
  isLoading: isCurrentLoading,
  isError: isErrorCurrent
} = useGetCurrentWeatherApiQuery(locationData?.locations?.[0]?.id, {
  skip: !city ? true : false
});

在Today.js中

//fetch data of one week, including today's highlight
const {
  data: weekData,
  isFteching: isFetchingWeek,
  isLoading: isLoadingWeek,
  isError: isErrorWeek
} = useGetWeekCastQuery(locationData?.locations?.[0]?.id, {
  skip: !city ? true : false
});
    
//fetch data for the upcoming 2 hours (every 15 min)
const {
  data,
  isFetching: isFetchingHourly,
  isLoading,
  isError: isErrorNow
} = useGetNowCastQuery(locationData?.locations?.[0]?.id, {
  skip: !city ? true : false
});

相关问题