#javascript #arrays #reactjs #react-native
#javascript #массивы #reactjs #реагировать-родной
Вопрос:
Итак, я использую react и у меня есть кнопка, которая добавляет элемент в массив с помощью useState, когда я нажимаю кнопку. Теперь, как только я добавил 1-й элемент, я хочу удалять первый элемент через каждые 1 секунду, пока массив не станет пустым, и я больше не нажимаю кнопку.
Я мог бы продолжать нажимать и добавлять элементы, но по прошествии 2 секунд, возможно, с помощью таймера, он также должен продолжать удаляться из передней части массива.
const [id, addId] = useState([])
<button onClick={()=> addId([...id,newId])> Add Id </button>
Итак, у меня есть представление, которое перебирает идентификаторы и рисует их на экране. Теперь, когда он будет нарисован, он должен исчезнуть, а также удаляться из массива после первого нажатия кнопки и удаления их каждые 2 секунды.
Я не могу правильно использовать setInterval, setTimeout и useEffect . Это заканчивается бесконечным циклом…
Допустим, я нажимаю 3 раза, массив будет [1,2,3], а через 1 секунду он должен быть [2,3], затем через 1 секунду он должен быть [3] и, наконец, остановиться с пустым массивом []. Как только я нажимаю кнопку, процесс удаления не должен останавливаться до тех пор, пока массив остается пустым, даже когда я перестаю нажимать на кнопку.
Надеюсь, теперь вам ясно, чего я хочу
Комментарии:
1. что не работает?
2. Я обновил выпуск @NinaScholz
Ответ №1:
Это мой удар по нему, хотя, возможно, он немного более грязный, чем хотелось бы:
(Редактировать: это основано на ссылке codesandbox КСТАТИ.)
import React from "react";
import "./styles.css";
export default function App() {
const delay = 2000;
const [ids, setIds] = React.useState([]);
const timeout = React.useRef(null);
const ids_length = React.useRef(null);
function set_remove_ids_timeout() {
if(timeout.current)
clearTimeout(timeout.current);
timeout.current = setTimeout(() => {
if (ids_length.current === 0) return;
ids_length.current = ids_length.current - 1;
setIds(([first, ...others]) => [...others]);
set_remove_ids_timeout();
}, delay);
}
function add() {
ids_length.current = ids.length 1;
setIds([...ids, (ids[ids.length - 1] || 0) 1]);
set_remove_ids_timeout();
}
React.useEffect(() => {
return () => { if(timeout.current) clearTimeout(timeout.current); };
}, []);
console.log("ids adding", ids);
return (
<div className="App">
<button onClick={add}> Add Id </button>
{ids.map((i) => (
<p key={i}>{i}</p>
))}
</div>
);
}
Редактировать… так что, возможно, я не понял / не понимаю, когда именно возобновляется удаление после нажатия кнопки… итак, если нажатие кнопки не должно влиять на задержку удаления, возможно, сработает что-то вроде этого:
import React from "react";
import "./styles.css";
export default function App() {
const delay = 2000;
const [ids, setIds] = React.useState([]);
const interval = React.useRef(null);
const ids_length = React.useRef(null);
function set_remove_ids_interval() {
if (!interval.current) {
interval.current = setInterval(() => {
console.log("ids_length.current", ids_length.current);
if (ids_length.current === 0) {
clearInterval(interval.current);
interval.current = null;
return;
}
ids_length.current = ids_length.current - 1;
setIds(([first, ...others]) => [...others]);
set_remove_ids_interval();
}, delay);
}
}
function add() {
ids_length.current = ids.length 1;
setIds([...ids, (ids[ids.length - 1] || 0) 1]);
set_remove_ids_interval();
}
React.useEffect(() => {
return () => {
if (interval.current) clearInterval(interval.current);
};
}, []);
console.log("ids adding", ids);
return (
<div className="App">
<button onClick={add}> Add Id </button>
{ids.map((i) => (
<p key={i}>{i}</p>
))}
</div>
);
}
Комментарии:
1. Привет, Бен Стивен, это работает для того, что мне было нужно, спасибо, но можно ли это сделать без использования ref ?
2. @Rishav Я думаю, вы могли бы попробовать объявить
let interval
иlet ids_length
вне области видимости компонента (как в ответе stellyrepsolka, но (я думаю) let вместо const ), но (согласно комментарию Эмиля Бержерона) это ограничивает вас использованием одного экземпляра компонента. Возможно, вы сможете использовать state для interval_id, но мне было бы интересно, если к моменту запуска функции, переданной в setInterval, interval_id внутри этой функции будет устаревшим, поэтому clearInterval(interval_id) не будет работать. Я думаю, что использование state для ids_length не будет работать, так как в интервальной функции оно, вероятно, устарело.3. @Rishav почему вы не хотите использовать ссылку?
4. Это работает, но я слышал, что использование ref — не лучший способ, и нам следует избегать его использования как можно чаще. Так хотел узнать, но большое спасибо, это работает!
Ответ №2:
Вы могли бы взять setInterval
и проверить, есть ли в массиве значения.
function add() {
array.push(Math.floor(Math.random() * 10));
document.getElementById('values').innerHTML = array.join(' ');
}
const array = [];
setInterval(() => {
if (!array.length) return;
array.shift();
document.getElementById('values').innerHTML = array.join(' ');
}, 1000);
<button onclick="add()">click</button><br>
<div id="values"></div>
Немного лучший подход, при котором интервал повторяется только в том случае, если в массиве есть хотя бы значение.
function showValues() {
document.getElementById('values').innerHTML = array.join(' ');
}
function add() {
array.push(Math.floor(Math.random() * 10));
showValues();
if (interval === undefined) interval = setInterval(intervalFn, 1000);
}
function intervalFn() {
array.shift();
showValues();
if (!array.length) {
clearInterval(interval);
interval = undefined;
}
}
let array = [],
interval;
<button onclick="add()">click</button><br>
<div id="values"></div>
Комментарии:
1. Это работает, но мой компонент не выполняет повторный рендеринг, поэтому я не могу видеть, как элементы исчезают. Мне это нужно для работы с React. codesandbox.io/s/busy-fermat-3nrrt?file=/src/App.js . Эффект должен быть похож на числа, которые я показываю внутри тега <p> .
2. извините, я понятия не имею о react .
3. Спасибо за это, действительно ценю это! Это начало. @Nina Scholz
Ответ №3:
const interval = null;
const Component = () => {
const [array, setArray] = useState([]);
useEffect(() =>
if (interval === null) {
interval = setInterval(() => {
if (array.length > 0) {
const [first, ...newArray] = array;
setArray(newArray);
}
}, 1000);
}
() => clearInterval(interval)
), []);
return ( put code that you want to render );
}
Инициализируйте интервальную переменную вне компонента, чтобы она не менялась, если в компоненте происходят какие-либо повторные рендеринги. В компоненте mount (use effect) установил interv, который будет срабатывать каждую секунду и удалять элемент из массива (shift или pop) и обновлять состояние. И при размонтировании очистите интервал.
Комментарии:
1.
.shift
изменяет массив на месте.2. да, я допустил ошибку. Я это исправил.
3. Кроме того, только что заметил, что
interval
это определено внеuseEffect
, что означает, что если когда-либо будет создан другой экземпляр компонента, он сломается.4. Только что понял, что вы используете
array
, который фиксируется при монтировании компонента и никогда не получает новых значений, так что это тоже не сработает. Вам нужно будет использовать параметр обратного вызова программы обновления:setArray(([first, ...newArray]) => newArray)