Моя цель:
Я пытаюсь создать компонент, который, когда вы дадите ему props.items и props.fadeEvery, он будет действовать как ротатор текста. В конце концов я хочу, чтобы он постепенно исчезал, но у меня проблемы с моим window.setInterval.
Возможная проблема:
Я вызываю setIndex в ловушке useEffect, но разве это не хорошая практика? Как я могу бесконечно перебирать элементы массива?
TextFade.tsx
// Imports: Dependencies
import React, { useState, useEffect } from 'react';
// TypeScript Type: Props
interface Props {
items: Array<string>,
fadeEvery: number,
};
// Component: Text Fade
const TextFade: React.FC<Props> = (props): JSX.Element => {
// React Hooks: State
const [ index, setIndex ] = useState<number>(0);
// React Hooks: Lifecycle Methods
useEffect(() => {
const timeoutID: number = window.setInterval(() => {
// End Of Array
if (index > props.items.length) {
// Set Data
setIndex(0);
}
else {
// Set Data
setIndex(index + 1);
}
}, props.fadeEvery * 1000);
// Clear Timeout On Component Unmount
return () => window.clearTimeout(timeoutID);
}, []);
return (
<div id = "text-fade-container">
<p id = "text-fade-text">{props.items[index]}</p>
</div>
);
};
// Exports
export default TextFade;
Еще одно предупреждение: если вы используете setInterval, то, скорее всего, лучше использовать clearInterval, а не clearTimeout. MDN действительно говорит, что идентификаторы являются общими, но для ясности лучше синхронизировать их.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Значения вашего индекса берутся из начального закрытия, и он не будет обновляться, пока не будет снова вызван обратный вызов useEffect. Вместо этого вы можете использовать функциональный способ обновления состояния
useEffect(() => {
const timeoutID: number = window.setInterval(() => {
// End Of Array
setIndex(prevIdx => {
if (prevIdx > props.items.length) {
// Set Data
return 0;
}
else {
// Set Data
return prevIdx + 1;
}
})
}, props.fadeEvery * 1000);
// Clear Timeout On Component Unmount
return () => window.clearTimeout(timeoutID);
}, []);
Отлично, что сработало! Спасибо за помощь!
Как сказал @Keith:
index is in what's called a closure. Your use effect has been told to only render once [].. So you need to use the callback version of setIndex
Итак, ваш хук useEffect будет:
useEffect(() => {
const timeoutID: number = window.setInterval(() => {
// End Of Array
if (index > props.items.length) {
// Set Data
setIndex(0);
} else {
// Set Data
setIndex(index + 1);
}
}, props.fadeEvery * 1000);
// Clear Timeout On Component Unmount
return () => window.clearTimeout(timeoutID);
}, [index]);
Вот рабочая демонстрация на CodeSandbox.
Не полагайтесь на оставшиеся комментарии. Их часто снимают.
ОК. Спасибо, @HereticMonkey.
Здесь используется зависимость useEffect, а не обратный вызов setState, и это, безусловно, еще один вариант. Вы могли бы даже тогда заменить setInterval на setTimeout. Если бы использование useEffect могло занять много времени, я бы даже сказал, что это лучший вариант.
Ты прав. Спасибо, @Keith.
Ниже я создал фрагмент, использующий версию обратного вызова setState, это позволяет избежать проблемы с закрытием, возникающей при использовании useEffect с [] ..
const {useState, useEffect} = React;
const TextFade = (props) => {
// React Hooks: State
const [ index, setIndex ] = useState(0);
// React Hooks: Lifecycle Methods
useEffect(() => {
const timeoutID: number = window.setInterval(() => {
// End Of Array
setIndex(index =>
index + 1 >= props.items.length
? 0
: index + 1);
}, props.fadeEvery * 1000);
// Clear Timeout On Component Unmount
return () => window.clearInterval(timeoutID);
}, []);
return (
<div id = "text-fade-container">
<p id = "text-fade-text">{props.items[index]}</p>
</div>
);
};
ReactDOM.render(<TextFade items = {['one','two', 'three']} fadeEvery = {1}/>, document.querySelector('#mount'));<script crossorigin src = "https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src = "https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id = "mount"></div>
indexнаходится в так называемом закрытии. Ваш эффект использования должен отображаться только один раз[].. Поэтому вам нужно использовать версию обратного вызоваsetIndex.