Я изучаю 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), я вижу, что он работает нормально.

What I want

Но если я 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>
                </>
            )}
        </>
    )
}
2
Bunny 16 Янв 2022 в 16:41
Почему бы вам не получить только часть ваших эпизодов, а не все. Думаю, это лучший подход.
 – 
merko
16 Янв 2022 в 16:49
@merko, спасибо, что дали мне несколько других способов, но действительно ли нет способа сделать это во внешнем интерфейсе? Я видел, как кто-то это делал, и я просто хочу попробовать. Примерно так: imgur.com/vnwnRFE
 – 
Bunny
16 Янв 2022 в 16:54

4 ответа

Вы устанавливаете информацию, когда компонент визуализируется. Затем вы проверяете, установлена ​​ли информация, и запускаете функцию chunkEpisodes. {info ? chunkEpisode() : console.log("")}. Информация будет устанавливаться каждый раз, поэтому вы постоянно используете функцию chunkEpisodes. Вместо того, чтобы запускать эту проверку, вы должны поместить ее в хук useEffect с информацией в качестве зависимости, и, если информация установлена, запустить функцию chunkEpisodes. Вот так:

useEffect(() => {
  if(info){
    chunkEpisode()
  }
}, [info])
0
Steve K 16 Янв 2022 в 16:53
Почему информация будет устанавливаться каждый раз?
 – 
MWO
16 Янв 2022 в 16:57
Потому что вы запускаете getInfo при первом монтировании компонента. Затем он устанавливает информацию и повторно отображает компонент. После этого информация будет установлена, поэтому вы запустите проверку, чтобы увидеть, установлена ​​ли информация, и она запускает функцию chunkEpisode. Который устанавливает список эпизодов и повторно отображает компонент. Затем ваша проверка запускается снова и видит, что информация установлена, и снова и снова запускает функцию chunkEpisode.
 – 
Steve K
16 Янв 2022 в 17:00
Я вижу, что информация устанавливается только один раз, и chunkEpisode() запускается каждый раз, потому что информация все еще установлена, а состояние EpisodeList изменилось - но, вероятно, мы имеем в виду одно и то же
 – 
MWO
16 Янв 2022 в 17:04
Если я это сделаю, он не сможет прочитать info.episodes.length, и я получу Can't perform a React state update on an unmounted component, что приведет к indicates a memory leak, и да, как сказал @MWO, я думаю, что info работает только однажды
 – 
Bunny
16 Янв 2022 в 17:05
1
Информация запускается только один раз и устанавливается один раз. Но каждый раз, когда вы вызываете setState(), компонент перерисовывается. Таким образом, ваша информация будет установлена ​​на этом этапе. Затем вы проверяете, установлена ​​ли информация, и вызываете функцию chunkEpisode() и вызываете функцию setState() для повторного рендеринга компонента. Затем ваш компонент выполняет повторный рендеринг и проверяет, установлена ​​ли информация. Это так, и вы снова и снова вызываете функцию chunkEpisode(). Что касается утечки памяти, вы уходите от этого компонента? Это происходит, когда вы пытаетесь установить состояние компонента, который больше недоступен.
 – 
Steve K
16 Янв 2022 в 17:35

В React каждый раз, когда изменяется какое-либо состояние, весь компонент перерисовывается.

Когда это произойдет, React пройдет через JSX и попытается обнаружить изменения в любом состоянии и повторно отобразить их.

Поскольку вы вызвали chunkEpisode() в JSX, это вызывает изменение состояния, то есть episodeList. Из-за изменения состояния JSX перерисовывается, снова вызывая chunkEpisode(). Это происходит бесконечно.

Лучшим подходом было бы вызвать chunkEpisode() с помощью хука useEffect.

useEffect(() => {
    if (info) {
      chunkEpisode()
    }
}, [info])

И удалите эту строку

{info ? chunkEpisode() : console.log("")}
0
Pawan Patel 16 Янв 2022 в 16:59

После получения ответа и настройки информации вызовите chunkEpisode() с данными, а не вызовите его из возврата.

const getInfo = async () => {
    const { data } = await instance.get("/getInfo?slug=" + slug)
    setInfo(data.data)
    chunkEpisode(data.data)
    setDone(true)
}
0
Rejo Mathew 16 Янв 2022 в 17:06

Прочитав некоторые из приведенных выше ответов, я попытался сначала вместо создания 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>
                </>
            )}
        </>
    )
}

Результат после того, как я изменил свой код таким образом.

Worked

Но, пожалуйста, дайте мне больше советов, чтобы оптимизировать это.

0
Bunny 16 Янв 2022 в 18:08