使用三元运算符取消重复显示的可能性

cancelling out possibility of repeat display using ternary operator

我正在使用 React 构建一个抽认卡应用程序,以帮助保留编程概念。到目前为止,我已将应用程序设置为在卡片正面显示带有概念 definition/explanation 并在背面显示相应的 term/concept 的卡片。用户可以翻转卡片并通过单击按钮更改为另一张卡片。问题是目前,onClick 有时会显示之前显示的卡片。我想防止这种情况发生。我尝试使用三元运算符这样做,但不知何故,我的 Javascript 逻辑是错误的,因为我仍然得到重复显示。我该如何解决这个问题?

代码如下:

// data and components
import { conceptArray } from "./data";
import FlashCard from "./components/FlashCard";

function App() {
  const [randomCard, setRandomCard] = useState({});
  const [mode, setMode] = useState(true);

  // this should store the individual concept (individual items in the concept Array) to later be displayed as a card
  const getCard = () => {
    // this changes the card and posits that there can be no repeat display of card that was displayed immediately before
    let newCard = conceptArray[Math.floor(Math.random() * conceptArray.length)];
    newCard !== randomCard ? setRandomCard(newCard) : newCard = conceptArray[Math.floor(Math.random() * conceptArray.length)];
    // this allows for the front of the card(ie. the definition) to be displayed
    setMode(true);
  };

  const flip = () => {
    // this allows for the back of the card (ie. the term itself) to be displayed
    setMode(!mode);
  }

  console.log(randomCard);

  return (
    <div className="App">
      <header className="App-header">
        <FlashCard randomCard={randomCard} mode={mode} />
        <button onClick={getCard}>Get FlashCard</button>
        <button onClick={flip}>Flip</button>
      </header>
    </div>
  );
}

export default App;

如果您参考 this 关于 SOF 的问题,可以应用许多解决方案。 请检查这个函数,如果你需要随机数组,也许你可以全局使用它,只需传递数组长度

const generate_random_number = (_objLength) => {

let random_array_of_integers = [];

while (random_array_of_integers.length < _objLength) {
    let random_integer = Math.floor(Math.random() * _objLength);
    if (!random_array_of_integers.includes(random_integer))
        random_array_of_integers.push(random_integer);
}


  return random_array_of_integers;

}

所以我最终接受了将 conceptArray 改组为状态的建议。我使用 Fisher-Yates(又名 Knuth)Shuffle 这样做,可以在这里找到:How to randomize (shuffle) a JavaScript array?。我还不完全理解它背后的逻辑,但我能够将它应用到我的代码中并让它工作。现在,卡片以随机顺序抽取,不会立即重复。

正如 Thomas Wikman 如此简洁地解释的那样,这就像洗牌 conceptArray 中的项目,类似于洗牌的方式。一旦发生这种情况,我使用 onClick 从该数组中获取第一个项目,过滤掉数组以排除被获取的概念,然后继续获取另一个。完成数组中的最后一个概念后,我会再次执行随机播放并重新开始。

如果这对其他人有帮助,这里是结果代码:

// data and components
import { conceptArray } from "./data";
import FlashCard from "./components/FlashCard";
import AddForm from "./components/AddForm";

function App() {
  // Fisher-Yates (aka Knuth) Shuffle
  // don't completely understand it but I got it to work
  const shuffle = array => {
    var currentIndex = array.length, temporaryValue, randomIndex;

    // While there remain elements to shuffle...
    while (0 !== currentIndex) {

      // Pick a remaining element...
      randomIndex = Math.floor(Math.random() * currentIndex);
      currentIndex -= 1;

      // And swap it with the current element.
      temporaryValue = array[currentIndex];
      array[currentIndex] = array[randomIndex];
      array[randomIndex] = temporaryValue;
    }

    return array;
  }

  // puts shuffled array of concepts into state
  const [cardArray, setCardArray] = useState(shuffle(conceptArray));
  const [randomCard, setRandomCard] = useState({});
  const [frontMode, setFrontMode] = useState(true);
  const [formMode, setFormMode] = useState(false);

  // stores the individual concept (individual item in the concept Array) to be displayed as a card
  const getCard = () => {
    // grabs the first item in the shuffled array
    setRandomCard(cardArray[0]);
    // filters array so that item already displayed on card cannot be shown again until array is reshuffled
    setCardArray(cardArray.filter(item => item !== cardArray[0]));
    // when there is only one item left in filtered array, shuffles entire array again
    if (cardArray.length === 1) {
      setCardArray(shuffle(conceptArray));
    }
    // this allows for the front of the card(ie. the definition) to be displayed
    setFrontMode(true);
  };

  const flip = () => {
    // this allows for the back of the card (ie. the term itself) to be displayed
    setFrontMode(!frontMode);
  }

  const renderForm = () => {
    setFormMode(true);
  }

  // console.log(randomCard);
  // console.log(conceptArrayRandomized);
  // console.log(conceptArray);


  return (
    <div className="App">
      {!formMode && <FlashCard randomCard={randomCard} frontMode={frontMode} />}
      {!formMode && <button onClick={getCard}>Get FlashCard</button>}
      {!formMode && <button onClick={flip}>Flip</button>}
      <br />
      {!formMode && <button onClick={renderForm}>Add New</button>}
      {formMode && <AddForm />}
    </div>
  );
}

export default App;