#javascript #reactjs
#javascript #reactjs
Вопрос:
Я пытался создать компонент, похожий на Instagram stories, в течение нескольких часов. Это не что иное, как интерактивная карусель, в которой вы можете перемещаться вперед и назад. Дело в том, что моя стратегия с setTimeout срабатывает каждый раз, когда я взаимодействую, и не отменяет состояние, поэтому, если пользователь находится на первой фотографии и нажимает 5 раз подряд, чтобы перейти к шестой фотографии, через несколько секунд settimeout будет подобен цунами, и компонент продвигает 5 историй подряд, начиная с 6. Я хотел каким-то образом сбрасывать свой таймер каждый раз, когда пользователь нажимает на новое изображение. Кажется сумасшедшим или это возможно?
Я не знаю, хватит ли у кого-нибудь терпения просмотреть код, если нет, то знает ли кто-нибудь хотя бы новый подход. Большое вам спасибо, я пытался решить эту проблему в течение некоторого времени.
function Stories(){
const files = [ 'image1.jpg', 'image2.jpg' , 'video3.mp4' ]
const videoRef = useRef()
const imageRef = useRef()
const [ index , setIndex ] = useState(0); // the index starts at 0... first file...
const [ focus, setFocus ] = useState(null);
// Every time the index of the storie is changed. This effect happens.
useEffect(() => {
const video = videoRef.current;
const image = imageRef.current;
if( files[index].includes('mp4')){
video.style.display = "inline"
video.src = files[index];
// when i put it i put something in the "setFocus"
// triggers a useEffect for the "focus"
// that makes the setTimeOut which in the end runs
// this same script again after a certain time.
setFocus(files[index]);
// if there is any image in the DOM, hide it
if( image ) {
image.style.display = "none";
}
//In case the files [index] is an image.
} else {
image.style.display = "inline"
image.src = files[index];
setFocus(files[index])
// if there is any video in the DOM, hide it
if ( video ){
video.style.display = 'none';
video.muted = true
}
}
},[index])
function back(){
if( index <= 0 ) return
setIndex( index - 1);
}
function next(){
if ( index < files.length - 1) setIndex( index 1 );
}
// This useeffect fires every time a file comes into focus.
useEffect(() => {
const timelapse = 5000;
// I wait for the video to finish to advance to the next index
if( files[index].includes('mp4')){
videoRef.current.addEventListener('ended',() => setIndex( index 1 ));
}
// If it's an image, so..
else {
setTimeout(() => {
if( focus !== null amp;amp; index < files.length - 1){
setIndex(index 1);
}
}, timelapse )
}
},[focus])
// HTML
return <>
<video ref={videoRef}/>
<img ref={imageRef} />
<button onClick={back}> back </button>
<button onClick={next}> next </button>
</>
}
Ответ №1:
Вы можете записать идентификатор timeout
и отменить его следующим образом:
const timeout = setTimeout(() => { console.log("woo") }, 5000)
window.clearTimeout(timeout)
Таким образом, одним из подходов было бы сохранить это и отменить время ожидания при вызове ваших функций back
или next
.
Другим подходом было бы использовать state для записи оставшегося времени и использовать таймер ( setInterval
), который отсчитывает каждую секунду и продвигает слайд, если он достигает нуля. Затем при нажатии вы можете просто повторно установить оставшееся время на 5 секунд.
const CarouselExample = () => {
const [time, setTime] = useState(5)
const [slide, setSlide] = setState(1)
useEffect(() => {
const countdown = window.setInterval(() => {
setTime(timeRemaining => {
if (timeRemaining - 1 < 0) {
setSlide(prevSlide => prevSlide 1)
return 5
} else {
return timeRemaining - 1
}
})
}, 1000)
return () => {
window.clearInterval(countdown)
}
}, [])
return (
<div>
<div>Current Slide: {slide}</div>
<button
onClick={() => {
setTime(5)
setSlide(prev => prev 1)
}}
>
Next
</button>
</div>
)
}
Комментарии:
1. Мне понравился ваш второй подход, я постараюсь его реализовать. Как вы видите, представляется возможным создать некоторую логику, аналогичную моей, в которой таймер меняется в зависимости от того, является ли это видео, в котором я ожидаю событие ‘ended’ вместо установленного времени?
2. Иногда вы можете увидеть кое-что, материалы для пожилых людей.
3. Я понимаю логику, useEffect выполняется только один раз и к нему больше нет доступа. Так что вы вряд ли сможете остановить этот таймер, верно? Возможно, мне нужно попробовать что-то еще
4. Я пытаюсь до сих пор, ха-ха, я обнаружил, что событие «завершено» не создается для нескольких вызовов, в конечном итоге оно создает тот же эффект цунами. Мне придется что-то придумать, но я ценю ваши усилия
5. О, итак, для видео я бы установил оставшееся время равным длине видео, когда оно начнет воспроизводиться, а затем прослушал изменения в воспроизведении, чтобы сделать то, что наиболее подходит (например, если оно приостановлено, может быть, установить таймер на несколько секунд?)