Если вы вызываете setLetters, это не мутирующие буквы, поэтому у букв будет новое значение при следующем рендере, а не на следующей строке исполняемого кода. Это не имеет ничего общего с тем, что установщик является асинхронным c, но потому, что когда вы используете установщик состояний, значением состояния становится устаревшее закрытие
Вы можете сделать что-то вроде этого:
const addscore = () => {
const newLetters = letters.filter(
(item) => item.letter !== letters[0].letter
)
setletters(newLetters);
let randomnumber = Math.floor(
Math.random() * newLetters.length
);
if (randomnumber != 0) {
randomnumber--;
}
setarrnumber(randomnumber);
upplevel(randomnumber);
setcounter((counter) => counter + 10);
};