[ Ajuda ] [ RESOLVIDO ] Problema de hidratação no desenvolvimento usando Next.js, mas em produção funcionando normalmente - Problema persiste apenas no Firefox
Introdução:
Estou criando um mini-projeto para uso pessoal usando Next.js, Tailwindcss e armazenando dados simples no indexedDB
.
Eu sempre utilizo o chrome para desenvolvimento, mas hoje decidi usar o meu navegador principal para uso pessoal, o firefox. Logo o problema apareceu.
Problema:
O problema é relacionado a hidratação, onde diz que o conteúdo gerado no cliente não corresponde ao do servidor. Porém, este problema apenas persiste no Firefox, no chrome não tem nenhuma reclamação sobre, nem mesmo um aviso sobre.
Outro ponto é que no firefox, o site crasha uma vez que eu volto a página e retorno novamente. Por exemplo, eu tenho um menu com algumas opções de seleções. Eu seleciono a rota que eu quero, de primeira funciona tudo perfeitamente. Se eu voltar para o menu e selecionar esta rota novamente, o site crasha na tela de loading, problema este que não ocorre no chrome, apenas no firefox.
O erro apresentado é que o objecStore
não existe no indexedDB
, o que é esquisito, pois a primeira leitura funciona perfeitamente. Se eu apagar o banco de dados do indexedDB
, e recarregar a página, o database é criando normalmente, sem erros e ainda recupera os valores iniciantes normalmente.
Soluções que eu implementei, mas de nada adiantou:
Assim que o problema apareceu, pensei na compatibilidade entre navegadores, mas no MDN afirma que o indexedDB
é amplamente suportando. E isto confirma pois em produção tudo funciona perfeitamente, tirando o crash.
Implementei um maior controle de transactions
no indexedDB
, porém o mesmo erro persiste.
Implementei um hook dedicado a inicialização do indexedDB
, com um state
que é definido como true
quando o database for inicializado, mas mesmo assim persiste o erro. Código do hook abaixo:
'use client';
import { useEffect, useState } from "react";;
export interface databaseSignature {
databaseName: string;
version: number;
storeName: string;
indexName?: string;
initialValues?: boolean;
}
let dbInstance: IDBDatabase | null = null;
export const initializeDB = ({
databaseName,
version,
indexName,
storeName
}: databaseSignature): Promise<IDBDatabase> => {
if (dbInstance) return Promise.resolve(dbInstance);
return new Promise((resolve, reject) => {
const openDB = indexedDB.open(databaseName, version);
openDB.onupgradeneeded = function HandleUpgrade(event) {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains(storeName) && indexName) {
const store = db.createObjectStore(storeName, {
keyPath: 'id',
autoIncrement: true,
});
store.createIndex(indexName, indexName, { unique: true });
}
};
openDB.onsuccess = function HandleSuccess(event) {
dbInstance = (event.target as IDBOpenDBRequest).result;
resolve(dbInstance);
};
openDB.onerror = function HandleError(event) {
console.error("Failed to open IndexedDB:", (event.target as IDBRequest).error);
reject((event.target as IDBRequest).error);
};
});
};
const setInitialValues = async (db: IDBDatabase, storeName: string) => {
const transaction = db.transaction(storeName, "readwrite");
const store = transaction.objectStore(storeName);
const data = [
// aqui tem os valores iniciais...
];
data.forEach((item) => store.add(item));
return new Promise<void>((resolve, reject) => {
transaction.oncomplete = () => resolve();
transaction.onerror = () => reject(transaction.error);
});
};
export const useIndexedDB = ({
databaseName,
version,
initialValues,
indexName,
storeName,
}: databaseSignature) => {
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const setupDB = async () => {
try {
const db = await initializeDB({ databaseName, version, storeName, indexName });
if (initialValues) {
const transaction = db.transaction(storeName, "readonly");
const store = transaction.objectStore(storeName);
const countRequest = store.count();
countRequest.onsuccess = async () => {
if (countRequest.result === 0) {
await setInitialValues(db, storeName);
}
};
countRequest.onerror = function(err) {
const error = (err.target as IDBRequest).result;
console.error(error);
};
}
setIsReady(true);
} catch (error) {
console.error("Error setting up IndexedDB:", error);
}
};
setupDB();
}, [databaseName, version, initialValues, indexName, storeName]);
return { isReady };
};
Relacionado a hidratação, eu tentei de tudo, já revisei o código diversas vezes. Procurei issues relacionadas no Github, joguei no chat, analisei minuciosamente cada detalhe do código fornecido para a solução, mas era evidente que o código fornecido pelo o chat não era útil. Como em produção não gera erro de hidratação, e em desenvolvimento, ocorre apenas no Firefox, me leva a crer que é algo interno do Next.js.
Conclusão:
Isso já ocorreu com vocês? Não forneci mais código, mas eu acredito que não seria útil, afinal grande parte é apenas JSX
e estados de controle de modal. Alguns useEffect
para obter os valores iniciais.
solução:
A extensão CollorZilla estava, de alguma forma injetando scripts no site, causando o erro de hidratação. Ao desativar essa extensão, tudo voltou as normalidads. O problema só ocorreu no firefox porquê eu tinha instalado apenas no Firefox.
A Extensão DarkReander também provoca erro de hidratação.
Eu não entendi o motivo por trás do erro ocorrer apenas no desenvolvimento, mas eu acredito que seja porquê no desenvolvimento temos várias ferramentas como Eslint e uma tipagem poderosa do TypeScript, coisa que não existe do lado do cliente.