Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make rootlayout server component #515

Closed
56 changes: 56 additions & 0 deletions src/app/components/AppBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use client";

import React, { Suspense, useEffect } from "react";
import { usePathname } from "next/navigation";
import { useAppDispatch, useAppSelector } from "hooks";
import { detectBrowserLanguage } from "utils";
import { i18nSlice } from "state/i18nSlice";
import Cookies from "js-cookie";
import { Navbar } from "./NavBar";
import { Footer } from "./Footer";
import { DexterToaster } from "./DexterToaster";

// This subcomponent is needed to initialize browser language for the whole app
function AppBody({ children }: { children: React.ReactNode }) {
const dispatch = useAppDispatch();
const path = usePathname();

// Detect browser langauge
const { textContent } = useAppSelector((state) => state.i18n);
const supportedLanguages = Object.keys(textContent);
useEffect(() => {
const userLanguageCookieValue = Cookies.get("userLanguage");
if (userLanguageCookieValue) {
dispatch(i18nSlice.actions.changeLanguage(userLanguageCookieValue));
} else {
const browserLang = detectBrowserLanguage();
if (supportedLanguages.includes(browserLang)) {
dispatch(i18nSlice.actions.changeLanguage(browserLang));
}
}
}, [dispatch, supportedLanguages]);

return (
<body>
<DexterToaster toastPosition="top-center" />
<div
data-path={path}
className="h-screen prose md:prose-lg lg:prose-xl max-w-none flex flex-col"
>
<div className="flex flex-col justify-between min-h-[100vh] max-w-[100vw] overflow-x-hidden">
<Navbar />
{
// When using useSearchParams from next/navigation we need to
// wrap the outer component in a Suspense boundary, otherwise
// the build on cloudflare fails. More info here:
// https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
}
<Suspense>{children}</Suspense>
<Footer />
</div>
</div>
</body>
);
}

export default AppBody;
20 changes: 20 additions & 0 deletions src/app/components/StoreProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use client";

import React, { useEffect } from "react";
import { Provider } from "react-redux";
import { store } from "state/store";
import { initializeSubscriptions, unsubscribeAll } from "subscriptions";

const StoreProvider = ({ children }: { children: React.ReactNode }) => {
// Initialize store
useEffect(() => {
initializeSubscriptions(store);
return () => {
unsubscribeAll();
};
}, []);

return <Provider store={store}>{children}</Provider>;
};

export default StoreProvider;
78 changes: 5 additions & 73 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,90 +1,22 @@
"use client";

import "./styles/globals.css";
import StoreProvider from "components/StoreProvider";
import AppBody from "components/AppBody";

import { Footer } from "./components/Footer";
import { Navbar } from "./components/NavBar";
import { Provider } from "react-redux";
import { usePathname } from "next/navigation";
import { DexterToaster } from "./components/DexterToaster";
import { useEffect, Suspense } from "react";
import { initializeSubscriptions, unsubscribeAll } from "./subscriptions";
import { store } from "./state/store";

import { detectBrowserLanguage } from "./utils";
import { i18nSlice } from "./state/i18nSlice";

import Cookies from "js-cookie";
import { useAppDispatch, useAppSelector } from "hooks";
// TODO: add metadata https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration#step-2-creating-a-root-layout

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
// Initialize store
useEffect(() => {
initializeSubscriptions(store);
return () => {
unsubscribeAll();
};
}, []);

// TODO: after MVP remove "use client", fix all as many Components as possible
// to be server components for better SSG and SEO
// and use metadata https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration#step-2-creating-a-root-layout

return (
<html lang="en" data-theme="dark" className="scrollbar-none">
<head>
<title>DeXter</title>
</head>
<Provider store={store}>
<StoreProvider>
<AppBody>{children}</AppBody>
</Provider>
</StoreProvider>
</html>
);
}

// This subcomponent is needed to initialize browser language for the whole app
function AppBody({ children }: { children: React.ReactNode }) {
const dispatch = useAppDispatch();
const path = usePathname();

// Detect browser langauge
const { textContent } = useAppSelector((state) => state.i18n);
const supportedLanguages = Object.keys(textContent);
useEffect(() => {
const userLanguageCookieValue = Cookies.get("userLanguage");
if (userLanguageCookieValue) {
dispatch(i18nSlice.actions.changeLanguage(userLanguageCookieValue));
} else {
const browserLang = detectBrowserLanguage();
if (supportedLanguages.includes(browserLang)) {
dispatch(i18nSlice.actions.changeLanguage(browserLang));
}
}
}, [dispatch, supportedLanguages]);

return (
<body>
<DexterToaster toastPosition="top-center" />
<div
data-path={path}
className="h-screen prose md:prose-lg lg:prose-xl max-w-none flex flex-col"
>
<div className="flex flex-col justify-between min-h-[100vh] max-w-[100vw] overflow-x-hidden">
<Navbar />
{
// When using useSearchParams from next/navigation we need to
// wrap the outer component in a Suspense boundary, otherwise
// the build on cloudflare fails. More info here:
// https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
}
<Suspense>{children}</Suspense>
<Footer />
</div>
</div>
</body>
);
}
Loading