React application freezes using custom async/await function

I’m working on a personal project and are experiencing problems with my application freezing in certain situations. It loads correctly when I redirect the first time, but if I try to change something in my code and React then re-renders, my application just freezes. It has to do with something in regards to my async function that finds x number of random elements in a list and returns that. (Also makes sure that it does not choose the same as the user has chosen).

Can someone see any problems in my function that they think may cause these problems? I’ve created a codesandbox example that is almost identical to the code I’m using.

https://codesandbox.io/s/async-fire-z28xq?file=/src/App.js

Also here is my generate random function;

export async function getRandomFighters(amount, selected) {
  const dataCopy = [
    { id: 1, label: "Test1" },
    { id: 2, label: "Test2" },
    { id: 3, label: "Test3" },
    { id: 4, label: "Test4" },
    { id: 5, label: "Test5" }
  ];
  const fighters = [];

  while (fighters.length !== amount) {
    let randomIndex = Math.floor(Math.random() * dataCopy.length);
    const selectedIndex = dataCopy.findIndex((e) => e.id === selected.id);

    if (randomIndex === selectedIndex) {
      continue;
    }

    const randomFighter = dataCopy.splice(randomIndex, 1)[0];
    fighters.push(randomFighter);
  }

  return fighters;
}

The method is called within a componentDidMount;

    async componentDidMount() {
        const fighters = await getRandomFighters(4, this.props.selected_fighter);
        this.setState({opponents: fighters});
    }

Answer

A potential issue I see is that your while loop could potentially block the main thread when it keeps selecting a randomIndex that is equal to the found selectedIndex.

Here is a suggested edit to have a guaranteed O(n) runtime, where n is the “amount” of random selections you want to make.

  1. Remove matching selected object from list of possible random selections
  2. Create an array of length amount and map it to n “amount” random selections.

Code:

function getRandomFighters(amount, selected) {
  const dataCopy = [
    { id: 1, label: "Test1" },
    { id: 2, label: "Test2" },
    { id: 3, label: "Test3" },
    { id: 4, label: "Test4" },
    { id: 5, label: "Test5" }
  ].filter((el) => el.id !== selected.id); // <-- remove selected

  const fighters = [...Array(Math.min(amount, dataCopy.length)).keys()].map(
    () => {
      const randomIndex = Math.floor(Math.random() * dataCopy.length);
      return dataCopy.splice(randomIndex, 1).pop();
    }
  );

  return fighters;
}

Leave a Reply

Your email address will not be published. Required fields are marked *