O que o array de dependências do useEffect realmente controla?
Ele diz ao React quais valores o efeito lê, então o efeito só roda de novo quando um deles muda. Um array vazio significa rodar uma vez após a montagem; omitir o array por completo significa rodar após cada render, o que é como começam os loops infinitos. Deixar de fora um valor usado é a outra falha — o efeito fecha sobre um valor obsoleto e continua usando ele para sempre.