В вашем примере вы используете setState следующим образом
setQuotes([...quotes, msg])
И каждый раз, когда вы получаете сообщение, javascript движок пытается найти переменную "quotes" в области действия этой функции
function (msg) {
Он не может найти его здесь и перейти к области действия, где была определена эта функция (область действия компонента).
Для каждого вызова функции существует новая область действия. И реагировать на вызовы компонент функции для каждого рендера. Итак, ваша функция с «msg» в аргументах, где вы используете setQuotes, каждый раз использует состояние «quotes» с первого рендера.
В первом рендере у вас есть пустой массив.
Затем вы есть [...firstEmptyArray, firstMessage]
Тогда у вас есть [...firstEmptyArray, secondMessage]
Тогда у вас есть [...firstEmptyArray, thirdMessage].
Возможно, вы можете это исправить, если будете использовать setQuotes, как;
setQuotes(oldQuotes => [...oldQuotes, msg]);
Таким образом, он будет использовать предыдущее значение для расчета нового.
PS. Помните, что не следует вызывать функцию подписки непосредственно в каждом рендере и использовать ее в качестве эффекта-эффекта с правильным массивом зависимостей в качестве второго аргумента.