Я изо всех сил пытаюсь понять, что именно происходит с API Youtube.
Кажется, приложение, которое я разработал в React, постоянно отправляет запросы к API Youtube даже после завершения первоначального рендеринга. Мне пришлось вернуться и освежить свое понимание useEffect, и я понял, что он настроен правильно, а затем, даже после того, как я реализовал способ кэширования первоначальных запросов, похоже, он все еще отправляет запросы, судя по тому, что я могу сказать на вкладке сети, и очевидно, это съедает мою квоту.
Я начал замечать это после того, как разработал собственный крючок для хранения восьми очень специфических видео с YouTube. Это мой useVideos индивидуальный крючок:
import { useState, useEffect } from "react";
import youtube from "../apis/youtube";
const useVideos = () => {
const [videos, setVideos] = useState(() => {
const cachedVideos = localStorage.getItem("cachedVideos");
return cachedVideos ? JSON.parse(cachedVideos) : [];
});
useEffect(() => {
if (videos.length === 0) {
fetchSpecificVideos();
}
}, []);
const fetchSpecificVideos = async () => {
try {
const videoIds = [
"VIDEO_1",
"VIDEO_2",
"VIDEO_3",
"VIDEO_4",
"VIDEO_5",
"VIDEO_6",
"VIDEO_7",
"VIDEO_8",
];
const videoData = [];
for (const videoId of videoIds) {
const response = await youtube.get("/videos", {
params: {
id: videoId,
part: "snippet",
maxResults: 8,
},
});
if (response.data.items && response.data.items.length > 0) {
videoData.push(response.data.items[0]);
}
}
setVideos(videoData);
localStorage.setItem("cachedVideos", JSON.stringify(videoData));
} catch (error) {
console.error("Error fetching videos:", error);
}
};
return [videos, fetchSpecificVideos];
};
export default useVideos;
ПРИМЕЧАНИЕ. Я заменил фактические идентификаторы на те, которые вы видите выше.
В моем фактическом файле API у меня есть следующее:
import axios from "axios";
const KEY = "my-youtube-api-key";
export default axios.create({
baseURL: "https://www.googleapis.com/youtube/v3",
params: {
part: "snippet",
maxResults: 8,
key: KEY,
},
});
Я добавляю скриншот вкладки сети, возможно, я неправильно понимаю, что происходит, но для меня это выглядит как множество запросов всего на 8 очень конкретных видео, которые загружаются один раз при рендеринге приложения. Это происходит с реализацией localStorage или без нее:
Мне интересно, относится ли это к какому-то типу запоминания.
Коллега предположил, что если несколько (N) компонентов используют специальный крючок useVideo, то все эти компоненты инициируют асинхронный запрос, что имеет смысл, однако только родительский компонент приложения использует этот пользовательский крючок. Я размещаю здесь компонент App.js:
import React, { useState, useEffect } from "react";
import Header from "./Header";
import VideoList from "./VideoList";
import VideoDetail from "./VideoDetail";
import useVideos from "../hooks/useVideos";
const App = () => {
const [selectedVideo, setSelectedVideo] = useState(null);
const [videos, fetchSpecificVideos] = useVideos();
useEffect(() => {
if (videos.length === 0) {
fetchSpecificVideos();
}
}, [videos, fetchSpecificVideos]);
useEffect(() => {
if (videos.length > 0) {
setSelectedVideo(videos[0]);
}
}, [videos]);
return (
<div className = "ui container">
<div className = "ui inverted menu">
<Header />
</div>
<VideoList
onVideoSelect = {setSelectedVideo}
videos = {videos}
selectedVideo = {selectedVideo}
/>
<VideoDetail video = {selectedVideo} />
</div>
);
};
export default App;
Пользовательский хук не используется ни в каких других компонентах.
@СергейСосунов, это происходило без всякого localStorage участия. Я добавил localStorage для кэширования выбранных конкретных видео, чтобы они перестали загружаться, но, похоже, это не работает. Поможет ли это прояснить, что происходит? Заранее спасибо.
У вас есть «только монтаж», при котором эффект работает только при первоначальном рендеринге. Что в вашем коде возможно перемонтируется для повторного запуска вашего пользовательского useVideos хука? Пожалуйста, отредактируйте , включив в него полный минимально воспроизводимый пример соответствующего кода, с которым вы работаете.
@Daniel Я хотел сказать, что если ваше локальное хранилище пусто при первом посещении пользователя и несколько (N) компонентов используют ваш крючок, то все эти компоненты будут инициализировать (N * videos_count) асинхронные запросы к API YouTube, и только после этой асинхронности операция завершена - они ВСЕ запишут некоторые данные в локальное хранилище и только потом эти данные «кэшируются». Вот почему я предлагаю добавить контекст верхнего уровня для хранения данных и загрузки этих данных, чтобы у вас был только один верхний компонент, который отвечает за загрузку и сохранение данных. А затем просто используйте эти данные через перехватчик или useContext.
@СергейСосунов, ааа, спасибо за разъяснения. Теперь я понимаю, но, Сергей, чтобы внести ясность, только компонент приложения использует специальный крючок useVideo, поможет ли мне поделиться этим файлом?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Первая проблема: fetchSpecificVideos не объявлен с useCallback хуком и не используется в useEffect dependency array компоненте App. По этой причине - каждый раз, когда компонент приложения перерисовывается из-за какого-либо изменения состояния - функция fetchSpecificVideos воссоздается с новым reference и это заставляет useEffect повторно выполняться, и если повторная отрисовка и повторное выполнение происходят из-за какого-либо другого несвязанного изменения состояния. и до заполнения videos будут выполнены дополнительные вызовы API YouTube.
Если вам действительно нужно использовать этот метод вне хука — оберните fetchSpecificVideos с помощью useCallback или не помещайте его в массив зависимостей useEffect/useCallback:
const fetchSpecificVideos = useCallback(async () => {
try {
const videoIds = [
"VIDEO_1",
"VIDEO_2",
"VIDEO_3",
"VIDEO_4",
"VIDEO_5",
"VIDEO_6",
"VIDEO_7",
"VIDEO_8",
];
const videoData = [];
for (const videoId of videoIds) {
const response = await youtube.get("/videos", {
params: {
id: videoId,
part: "snippet",
maxResults: 8,
},
});
if (response.data.items && response.data.items.length > 0) {
videoData.push(response.data.items[0]);
}
}
setVideos(videoData);
localStorage.setItem("cachedVideos", JSON.stringify(videoData));
} catch (error) {
console.error("Error fetching videos:", error);
}
}, []);
Следующая и основная проблема: у вас есть useEffect для вызова fetchSpecificVideos if videos.length === 0 как внутри хука, так и внутри компонента приложения, который... избыточен и съедает вашу фактическую квоту. Было бы лучше, если бы крючок useVideos мог вызывать только этот метод, не экспортируйте fetchSpecificVideos из него, а просто вызывайте его внутри.
Также я продублирую свой комментарий здесь для будущих читателей:
если localstorage пуст, при первом посещении пользователя и несколько (N) компонентов используют ваш хук, тогда все эти компоненты будут инициализировать (N * videos_count) асинхронные запросы к API YouTube, и только после выполнения этой асинхронной операции - все они начнет записывать полученные данные в локальное хранилище, и только затем эти данные «кэшируются». Кроме того, все они будут вызывать метод setVideos с одинаковым, но разным массивом ссылок, что может повлиять на другие useEffects. Предлагается добавить контекст верхнего уровня, который будет загружать и сохранять данные, а затем просто использовать эти данные через перехватчик или useContext.
Вопрос: происходит ли описанное вами поведение при начальной загрузке приложения, когда данные локального хранилища пусты, или даже происходит после этого? Первоначально, когда localstorage пуст и
videosесть [] -fetchSpecificVideosпроизойдет столько раз, сколько вы использовали этот хук (для видимых/отображаемых компонентов). Чтобы сделать это лучше, вам нужно добавить контекст, который будет содержать все данные и функции, обернуть приложение этим контекстом и переписать этот крючок, чтобы он полагался на значения и функции в контексте, таким образом, он будет действовать близко к синглтону. .