【问题标题】:Add element to an array and remove the element from front, one at a time, every 2 seconds until the array is empty?将元素添加到数组并从前面删除元素,一次一个,每 2 秒一次,直到数组为空?
【发布时间】:2021-03-22 05:51:14
【问题描述】:

所以,我正在使用 react 并有一个按钮,当我按下按钮时,它使用 useState 将元素添加到数组中。现在,一旦我添加了第一个元素,我想在每 1 秒后删除第一个元素,直到数组为空并且我不再按下按钮。

我可以继续按下并添加项目,但在 2 秒过去后,可能使用计时器,它也应该继续从数组的前面移除。

const [id, addId] = useState([])

<button onClick={()=> addId([...id,newId])> Add Id </button> 

所以,我有一个循环遍历 ID 并将它们绘制在屏幕上的视图。现在,当它被绘制时,它应该会消失,并且在我第一次按下按钮并每 2 秒将其移除后,它也会从阵列中移除。

我无法正确使用 setInterval、setTimeout 和 useEffect。它最终陷入无限循环......

假设我按了 3 次,数组将是 [1,2,3],1 秒后,它应该是 [2,3],然后 1 秒后,它应该是 [3],最后停止一个空数组 []。一旦我按下按钮,即使我停止按下按钮,删除过程也不应该停止,直到数组为空。

希望现在清楚我想要什么

【问题讨论】:

  • 什么不起作用?
  • 我已经更新了@NinaScholz 的问题

标签: javascript arrays reactjs react-native


【解决方案1】:

您可以使用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>

【讨论】:

  • 这可行,但是我的组件没有重新渲染,所以我看不到元素消失。我需要这个来处理 React。 codesandbox.io/s/busy-fermat-3nrrt?file=/src/App.js。效果应该类似于我在

    标记中显示的数字。

  • 对不起,我不知道 react
  • 感谢您,非常感谢!这是一个开始。 @Nina Scholz
【解决方案2】:
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 );
}

在组件外部初始化间隔变量,因此如果组件中发生任何重新渲染,它不会改变。在组件中确实安装了(使用效果)设置交互,它将每秒触发并从数组中删除元素(移位或弹出)并更新状态。并在卸载时清除间隔。

【讨论】:

  • .shift 在原地改变数组。
  • 是的,我犯了一个错误。我修好了。
  • 另外,刚刚注意到interval 是在useEffect 之外定义的,这意味着如果创建了另一个组件实例,它就会中断。
  • 刚刚意识到您正在使用array,它会在组件的挂载上被捕获并且它永远不会获得新值,所以这也不起作用。您需要使用更新程序回调参数:setArray(([first, ...newArray]) =&gt; newArray)
【解决方案3】:

这是我的尝试,虽然它可能比我想要的有点混乱:

(编辑:这是基于代码和框链接顺便说一句。)

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>
  );
}

【讨论】:

  • 嘿 Ben Stephen,这可以满足我的需要,谢谢,但是,不使用 ref 可以吗?
  • @Rishav 我认为您可以尝试在组件范围之外声明 let intervallet ids_length (就像在 stellyrepsolka 的回答中一样,但是(我认为)让而不是 const)但是(根据 Emile Bergeron 的评论) 限制您使用该组件的一个实例。您可能可以对 interval_id 使用状态,但我想知道当传递给 setInterval 的函数运行时,该函数内部的 interval_id 是否会过时,因此 clearInterval(interval_id) 将不起作用。我认为对 ids_length 使用状态是行不通的,因为在区间函数中它很可能是陈旧的。
  • @Rishav 为什么你不想使用 ref?
  • 这行得通,但是,我听说使用 ref 不是最好的方法,我们应该尽可能避免使用它。所以想知道,但非常感谢,它有效!
猜你喜欢
  • 2016-07-05
  • 2013-09-16
  • 1970-01-01
  • 2012-12-02
  • 1970-01-01
  • 2012-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多