Реагируйте useEffect с useState и setInterval - PullRequest
0 голосов
/ 27 сентября 2019

, используя следующий код для поворота массива объекта через компонент DOM.Проблема в том, что состояние никогда не обновляется, и я не могу тренироваться, почему ..?

    import React, { useState, useEffect } from 'react'

const PremiumUpgrade = (props) => {
    const [benefitsActive, setBenefitsActive] = useState(0)


// Benefits Details
const benefits = [
    {
        title: 'Did they read your message?',
        content: 'Get more Control. Find out which users have read your messages!',
        color: '#ECBC0D'
    },
    {
        title: 'See who’s checking you out',
        content: 'Find your admirers. See who is viewing your profile and when they are viewing you',
        color: '#47AF4A'
    }
]


// Rotate Benefit Details
useEffect(() => {
    setInterval(() => {
        console.log(benefits.length)
        console.log(benefitsActive)

        if (benefitsActive >= benefits.length) {
            console.log('................................. reset')
            setBenefitsActive(0)
        } else {
            console.log('................................. increment')
            setBenefitsActive(benefitsActive + 1)
        }
    }, 3000)
}, [])

вывод, который я получаю, выглядит следующим образом.Я вижу, что useState 'setBenefitsActive' вызывается, но 'BenefitsActive' никогда не обновляется.

enter image description here

Ответы [ 3 ]

6 голосов
/ 27 сентября 2019

Вы не передаете никаких зависимостей в useEffect, что означает, что он будет выполняться только один раз, в результате чего параметр для setInterval будет только когда-либо получать начальное значение benefitsActive (которое в данном случае равно 0).

Вы можете изменить существующее состояние, используя функцию, а не просто устанавливая значение, например

setBenefitsActive(v => v + 1);
1 голос
/ 27 сентября 2019

Когда вы передаете функцию в setInterval, вы создаете замыкание, которое запоминает начальное значение benefitsActive.Одним из способов обойти это является использование ссылки:

  const benefitsActive = useRef(0);

  // Rotate Benefit Details
  useEffect(() => {
    const id = setInterval(() => {
      console.log(benefits.length);
      console.log(benefitsActive.current);

      if (benefitsActive.current >= benefits.length) {
        console.log("................................. reset");
        benefitsActive.current = 0;
      } else {
        console.log("................................. increment");
        benefitsActive.current += 1;
      }
    }, 3000);

    return () => clearInterval(id);
  }, []);

Демо: https://codesandbox.io/s/delicate-surf-qghl6

1 голос
/ 27 сентября 2019

Код для вашей пользы!В своем эффекте использования, как предложил @James, добавьте зависимость к обновляемой переменной.Также не забудьте очистить свой интервал, чтобы избежать утечек памяти!

// Rotate Benefit Details
useEffect(() => {
    let rotationInterval = setInterval(() => {
        console.log(benefits.length)
        console.log(benefitsActive)

        if (benefitsActive >= benefits.length) {
            console.log('................................. reset')
            setBenefitsActive(0)
        } else {
            console.log('................................. increment')
            setBenefitsActive(benefitsActive + 1)
        }
    }, 3000)

    //Cleanup can be done like this
    return () => {
        clearInterval(rotationInterval);
    }
}, [benefitsActive]) // Add dependencies here 

Рабочая песочница: https://codesandbox.io/s/react-hooks-interval-demo-p1f2n

РЕДАКТИРОВАТЬ

Как указаноДжеймсом, это может быть лучше достигнуто с помощью setTimeout, который является более чистой реализацией.

// Rotate Benefit Details
useEffect(() => {
    let rotationInterval = setTimeout(() => {
        console.log(benefits.length)
        console.log(benefitsActive)

        if (benefitsActive >= benefits.length) {
            console.log('................................. reset')
            setBenefitsActive(0)
        } else {
            console.log('................................. increment')
            setBenefitsActive(benefitsActive + 1)
        }
    }, 3000)


}, [benefitsActive]) // Add dependencies here 

Здесь своего рода интервал создается автоматически из-за вызова useEffect после каждого setTimeout, создающего замкнутый цикл.

Если вы все еще хотите использовать интервал, хотя очистка обязательна, чтобы избежать утечек памяти.

...