Я изучаю ReactJS и Javascript, и у меня возникла эта проблема, я не знаю, почему она так себя ведет, но я думаю, что знаю, что моя функция рендерится слишком много раз, устанавливая состояние для setEpisodeList
снова и снова я думаю, как мне разрешить это в моем коде?
Состояние info
содержит объект episodes
, имеющий тип массива.
У меня есть более 1000 эпизодов в 1 массиве, поэтому я хочу разбить этот 1 большой массив episodes
, содержащий более 1000 эпизодов, на более мелкие фрагменты, каждый фрагмент будет массивом, содержащим 10 эпизодов, поэтому я используя episodeListChunk.push(info.episodes.splice(0, 10))
.
Так что это станет
[
[chunk 1 - 10 episodes],
[chunk 2 - 10 episodes],
[chunk 3 - 10 episodes],
[chunk 4 - 10 episodes], ...
]
Если я использую console.log(episodeListChunk)
, я вижу, что он работает нормально.
Но если я setEpisodeList(episodeListChunk)
, то я получаю слишком много повторных рендеров. Означает ли это, что каждый раз, когда запускается цикл while, он каждый раз устанавливает состояние? Если это так, то какой подход я должен использовать, чтобы справиться с этими ситуациями?
Вот мой код:
function MovieWatch({ instance }) {
const [info, setInfo] = useState([])
const [done, setDone] = useState(false)
const [episodeList, setEpisodeList] = useState([])
let { slug } = useParams()
const getInfo = async () => {
const { data } = await instance.get("/getInfo?slug=" + slug)
setInfo(data.data)
setDone(true)
}
useEffect(() => {
getInfo()
}, [])
const chunkEpisode = () => {
const episodeListChunk = []
while (info.episodes.length) {
episodeListChunk.push(info.episodes.splice(0, 10))
}
setEpisodeList(episodeListChunk)
}
return (
<>
<div>Hello info page</div>
{!done ? (
<Loading loading={true} background="#000000" loaderColor="#FFFF00" />
) : (
<>
{info ? chunkEpisode() : console.log("")}
<div>Done loading</div>
</>
)}
</>
)
}
4 ответа
Вы устанавливаете информацию, когда компонент визуализируется. Затем вы проверяете, установлена ли информация, и запускаете функцию chunkEpisodes. {info ? chunkEpisode() : console.log("")}
. Информация будет устанавливаться каждый раз, поэтому вы постоянно используете функцию chunkEpisodes. Вместо того, чтобы запускать эту проверку, вы должны поместить ее в хук useEffect с информацией в качестве зависимости, и, если информация установлена, запустить функцию chunkEpisodes. Вот так:
useEffect(() => {
if(info){
chunkEpisode()
}
}, [info])
info.episodes.length
, и я получу Can't perform a React state update on an unmounted component
, что приведет к indicates a memory leak
, и да, как сказал @MWO, я думаю, что info
работает только однажды
setState()
, компонент перерисовывается. Таким образом, ваша информация будет установлена на этом этапе. Затем вы проверяете, установлена ли информация, и вызываете функцию chunkEpisode()
и вызываете функцию setState()
для повторного рендеринга компонента. Затем ваш компонент выполняет повторный рендеринг и проверяет, установлена ли информация. Это так, и вы снова и снова вызываете функцию chunkEpisode()
. Что касается утечки памяти, вы уходите от этого компонента? Это происходит, когда вы пытаетесь установить состояние компонента, который больше недоступен.
В React каждый раз, когда изменяется какое-либо состояние, весь компонент перерисовывается.
Когда это произойдет, React пройдет через JSX и попытается обнаружить изменения в любом состоянии и повторно отобразить их.
Поскольку вы вызвали chunkEpisode()
в JSX, это вызывает изменение состояния, то есть episodeList
. Из-за изменения состояния JSX перерисовывается, снова вызывая chunkEpisode()
. Это происходит бесконечно.
Лучшим подходом было бы вызвать chunkEpisode()
с помощью хука useEffect
.
useEffect(() => {
if (info) {
chunkEpisode()
}
}, [info])
И удалите эту строку
{info ? chunkEpisode() : console.log("")}
После получения ответа и настройки информации вызовите chunkEpisode() с данными, а не вызовите его из возврата.
const getInfo = async () => {
const { data } = await instance.get("/getInfo?slug=" + slug)
setInfo(data.data)
chunkEpisode(data.data)
setDone(true)
}
Прочитав некоторые из приведенных выше ответов, я попытался сначала вместо создания 1 большого массива, а затем просто соединял его всякий раз, когда впервые получал этот большой массив, я не знаю, правильно ли мое объяснение или нет. ..
Из того, что @Rejo Mathew может использовать chunkEpisode(data.data)
, тогда мне интересно, в чем смысл установки состояния info
, поэтому я удалил его, и из @Steve K с объяснением @MWO я попытался это сделать. Теперь он работает нормально.
Но все же, я все равно не хочу так делать, мне кажется странным, чем первый, потому что первый мне кажется крутым, странно, почему он не работает.
Вместо setInfo
затем используйте этот info
для реализации в функции, я просто использую эти данные, которые я получил, не устанавливая их, затем объединяю chunkEpisode
в функцию getInfo
, затем используйте useEffect
, чтобы вызвать его только один раз, я думаю.
Вот код после того, как я это сделаю:
function MovieWatch({ instance }) {
const [done, setDone] = useState(false)
const [episodeList, setEpisodeList] = useState([])
let { slug } = useParams()
const getInfo = async () => {
const { data } = await instance.get("/getInfo?slug=" + slug)
const episodeListChunk = []
while (data.data.episodes.length) {
episodeListChunk.push(data.data.episodes.splice(0, 10))
}
setEpisodeList(episodeListChunk)
setDone(true)
}
useEffect(() => {
getInfo()
}, [])
return (
<>
<div>Hello info page</div>
{!done ? (
<Loading loading={true} background="#000000" loaderColor="#FFFF00" />
) : (
<>
{console.log(episodeList)}
<div>Done loading</div>
</>
)}
</>
)
}
Результат после того, как я изменил свой код таким образом.
Но, пожалуйста, дайте мне больше советов, чтобы оптимизировать это.
Похожие вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript / JS) и его различных диалектах / реализациях (кроме ActionScript). Включите все соответствующие теги в свой вопрос; например, [node.js], [jquery], [json] и т. д.