Недавно мне было поручено создать компонент уведомления, который на несколько секунд будет отображать сообщение, а затем исчезать. Это звучало нормально, пока я не узнал, что на самом деле нужно разместить массив сообщений.

Вот пример использования setTimeout() с одиночным уведомлением:

Обратите внимание, что ловушка useEffect() запускается всякий раз, когда message изменяется. И message устанавливается на '' внутри setTimeout().

Чтобы обрабатывать несколько уведомлений, я начал с приведенного выше кода. У меня запускался хук useEffect() всякий раз, когда messages (теперь массив) изменялся. И messages теперь был установлен в: messages минус первый элемент внутри setTimeout(). Выглядело это примерно так:

Что на самом деле происходит, когда вы запускаете это, немного удивляет:

  1. Нажата кнопка.
  2. Компоненты "Приложение" и "Уведомления" повторно визуализируют и отображают обновленный файл messages.
  3. clearTimeout() выполняется.
  4. setTimeout() выполняет закрытие.
  5. Первый / единственный элемент удаляется из массива messages.
  6. setMessages() выполнено.
  7. Компонент приложения повторно отрисовывается, поскольку его состояние изменилось.

Обратите внимание, что компонент "Уведомления" не повторно обрабатывается.

Если вы удалите clearTimeout() компонент уведомлений будет повторно отображен, но все станет беспорядочно. (Рекомендую попробовать!)

Итак, как нам это исправить?

Вот мое решение:

В этом решении ловушка useEffect() по-прежнему выполняется при изменении messages. Вы также заметите, что messages все еще устанавливается внутри setTimeout().

Разница в том, что я перестаю ссылаться на messages и изменять его внутри setTimeout(). Вместо этого я создаю msgs (копию messages), изменяю его и устанавливаю messages.

Что происходит в этом растворе:

  1. При начальной загрузке выполняется useEffect(), включая clearTimeout(). Но messages и msgs равны [].
  2. Нажата кнопка.
  3. setMessages()executes, что вызывает повторную визуализацию компонентов приложения и уведомлений.
  4. useEffect() и clearTimeout()execute.
  5. setTimeout() выполняется, удаляя первый элемент из msgs и устанавливая messages на измененный msgs.
  6. Изменение состояния вызывает повторную визуализацию компонентов приложения и уведомлений.
  7. useEffect() выполняется, и цикл продолжается до тех пор, пока условие msgs.length не перестанет выполняться.

Надеюсь, это поможет любому, кто борется с подобной проблемой. Хотелось бы услышать ваши решения!