Q: Необъяснимое несрабатывание функции Всем привет.
Делаю простой слайдер. Слайд, по бокам кнопки, при нажатии на кнопки переключаем на предыдущий или на следующий слайд. Ходит по кругу.
Разумеется к кнопкам привязаны функции (nextSlide, prevSlide), которые по нажатию на иконку, меняют индекс записанный в state, в результате отображается следующий или предыдущий слайд.
С нажатием на иконки мышкой все прекрасно работает.
Захотел чтобы еще и с клавиатуры срабатывало. Вешаю слушатель на window и по событию keydown вызываю функцию, которая вызывает nextSlide (нажатие на стрелку вправо или пробел) или prevSlide (нажатие на стрелку влево). И здесь наблюдается странный глюк. Все корректно срабатывает при первом вызове функции prevSlide или nextSlide по нажатию кнопки на клавиатуре. Если повторно вызывается та же функция, она вызывается (срабатывает console.log), но по необъяснимой причине не изменяет стэйт, соответственно слайд также не меняется.
Почему? Что не так?
import { FaArrowAltCircleRight, FaArrowAltCircleLeft } from 'react-icons/fa';

export const Carousel = ({ slides }) => {
  const [current, setCurrent] = useState(0);
  const length = slides.length;

  useEffect(() => {
    window.addEventListener('keydown', handleKeyDown);
    return () => {
      window.removeEventListener('keydown', handleKeyDown)
    }
  }, [])

  useEffect(() => {
    console.log('current', current)
  }, [current])

  const nextSlide = () => {
    console.log('nextSlide')
    setCurrent(current === length - 1 ? 0 : current + 1)
  }

  const prevSlide = () => {
    console.log('prevSlide')
    setCurrent(current === 0 ? length - 1 : current - 1)
  }

  const handleKeyDown = (e) => {
    console.log('handleKeyDown', e.code)
    switch (e.code) {
      case 'Space': {
        nextSlide();
        break;
      }

      case 'ArrowRight': {
        nextSlide();
        break;
      }

      case 'ArrowLeft': {
        prevSlide();
        break;
      }

      default:
        break;
    }
  }

  return (
    <section className='slider'>
      <FaArrowAltCircleLeft className='left-arrow' onClick={prevSlide} />
      {
        slides.map((slide, index) => {
          return (
            <div className={index === current ? 'slide active' : 'slide'} key={index}>
              {index === current && (
                <img src={slide} alt='travel' className='image' />
              )}
            </div>
          )
        })
      }
      <FaArrowAltCircleRight className='right-arrow' onClick={nextSlide} />
    </section>
  )
} ``` 


A: 
по необъяснимой причине не изменяет стэйт, соответственно слайд также не меняется

Причина сводится к тому что функция назначенная в useEffect без зависимостей не видит измененную переменную стейта. Для нее она всегда одна и та же.
Предложу пару вариантов решения проблемы


const {useState, useEffect} = React

const Carousel = _ => {
  const [current, setCurrent] = useState(0);
  const length = 10;


  useEffect(() => {
    console.log('current', current)
  }, [current])

  const nextSlide = () => {
    console.log('nextSlide')
    setCurrent(old => old === length - 1 ? 0 : old + 1)
  }

  const prevSlide = () => {
    console.log('prevSlide')
    setCurrent(old => old === 0 ? length - 1 : old - 1)
  }

  const handleKeyDown = (e) => {
    console.log('handleKeyDown', e.code)
    switch (e.code) {
      case 'Space': {
        nextSlide();
        break;
      }

      case 'ArrowRight': {
        nextSlide();
        break;
      }

      case 'ArrowLeft': {
        prevSlide();
        break;
      }

      default:
        break;
    }
  }
  useEffect(_ => {
    window.addEventListener('keydown', handleKeyDown);
    return _ => {
      window.removeEventListener('keydown', handleKeyDown)
    }
  }, [])

  return (
    <section className='slider'>
      <button onClick={prevSlide}>Уменьшить</button>
      <p>{current}</p>
      <button onClick={nextSlide}>Увеличить</button>
    </section>
  )
}

const domContainer = document.querySelector('#root');
const root = ReactDOM.createRoot(domContainer);
root.render(<Carousel />);
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
  
<div id='root'></div>


Так же еще можно и вот так сделать


const {useState, useEffect} = React

const Carousel = _ => {
  const [current, setCurrent] = useState(0);
  const length = 10;

  const nextSlide = () => {
    console.log('nextSlide')
    setCurrent(current === length - 1 ? 0 : current + 1)
  }

  const prevSlide = () => {
    console.log('prevSlide')
    setCurrent(current === 0 ? length - 1 : current - 1)
  }

  const handleKeyDown = (e) => {
    console.log('handleKeyDown', e.code)
    switch (e.code) {
      case 'Space': {
        nextSlide();
        break;
      }

      case 'ArrowRight': {
        nextSlide();
        break;
      }

      case 'ArrowLeft': {
        prevSlide();
        break;
      }

      default:
        break;
    }
  }

  useEffect(_ => {
    console.log('current', current)
    window.addEventListener('keydown', handleKeyDown);
    return _ => {
      window.removeEventListener('keydown', handleKeyDown)
    }
  }, [current])
  
  
  return (
    <section className='slider'>
      <button onClick={prevSlide}>Уменьшить</button>
      <p>{current}</p>
      <button onClick={nextSlide}>Увеличить</button>
    </section>
  )
}

const domContainer = document.querySelector('#root');
const root = ReactDOM.createRoot(domContainer);
root.render(<Carousel />);
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
  
<div id='root'></div>


