Gatsby - Layout in wrapPageElement does not update state - javascript

I am having issues with wrapPageElement in my Gatsby 4 project.
wrapPageElement.js
import React from 'react';
import { PathProvider } from './src/providers/path';
import Layout from './src/components/layout/layout';
// Pass all props (hence the ...props)
// to the layout component so it has access to things like pageContext or location
const wrapPageElement = ({ element, props: { location } }) => (
<PathProvider path={location.pathname}>
<Layout>{element}</Layout>
</PathProvider>
);
export default wrapPageElement;
I am using wrapPageElement to reuse my Layout across all pages but the Layout does not update its React state properly when included via wrapPageElement. If I render Layout within my pages instead, it works fine.
gatsby-brower.js
import './src/styles/global.css';
import CustomProviders from './wrapWithProvider';
import CustomLayout from './wrapPageElement';
export const wrapPageElement = CustomLayout;
export const wrapRootElement = CustomProviders;
A view components down in my navbar within Layout I am calling:
const { language, defaultLanguage } = useI18next();
console.log('locale', language);
When I am changing my page language with I18next, "locale" does not change value. "locale" does however change its value to the updated language if I am rendering the layout within my page.
I should note that I am using TypeScript for all of my project except the Gatsby lifecycle files, e.g. wrapPageElement.js
Any help is appreciated! Not sure where exactly the root cause could be at?
Additional code
Layout component
return (
<div className="page">
<Helmet>
<meta httpEquiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=0" />
<meta name="description" content={props.description || description} />
<meta name="image" content={props.imagePath || image} />
<title>{props.title || title}</title>
{props.noIndex && <meta name="robots" content="noindex" />}
</Helmet>
<Navbar />
<main className="m-10 mt-60">{children}</main>
<Footer />
</div>
);
};
Navbar component
return (
<nav className="bg-green-300 w-full fixed top-0">
<ul className="flex flex-row gap-5 m-10">
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li className="ml-auto">
<ChangeLanguageLink language="en">EN</ChangeLanguageLink>
</li>
<li>
<ChangeLanguageLink language="th">TH</ChangeLanguageLink>
</li>
</ul>
</nav>
);
ChangeLanguageLink component
const ChangeLanguageLink: React.FC<React.PropsWithChildren<ChangeLanguageLinkProps>> = ({ children, ...props }) => {
const { originalPath } = useI18next();
return (
<LocalizedLink {...props} to={addFinalSlash(originalPath)}>
{children}
</LocalizedLink>
);
};

What I extract is that you are trying to use I18next to update the state but you may not need to wrap your whole application to achieve that. The const { language, defaultLanguage } = useI18next(); will give you the current language.
That said, if you keep wanting your approach, wrapPageElement and wrapRootElement are wrappers (hence the name) that are shared between Gatsby SSR and Gatsby Browser so in the gatsby-ssr.js you need to add the exact information:
import CustomProviders from './wrapWithProvider';
import CustomLayout from './wrapPageElement';
export const wrapPageElement = CustomLayout;
export const wrapRootElement = CustomProviders;
Keep in mind that wrapRootElement is a wrapper that is never unmounted across the application.

Related

Next.js Variables reset after i switch pages

Im trying to simply have a shared variable between my pages in my Next.Js application.. My _app.js below contains the following..
import { useState } from 'react';
const CustomApp = ({ Component, pageProps }) => {
// Variables
const [testVariable, setTestVariable] = useState(0)
// globalFunctions
const setTestVariable = (newValue) => setLocalVariable(newValue);
return (
<Component
{...pageProps}
// Export Variables
testVariable={testVariable}
// Export Functions
setTestVariable={setTestVariable}
/>
);
}
export default CustomApp
I have two pages... Both are the same except one is called index.js and exports Home, the other is called about.js and exports About...
import { useState, useEffect } from 'react'
import 'bulma/css/bulma.css'
import styles from '../styles/Home.module.css'
const Home = ({ testVariable, setTestVariable, }) => {
useEffect(() => {
})
return (
<div id={styles.outerDiv}>
<Head>
<title>My Next.Js Page</title>
<meta name="description" content="A Next.js application" />
</Head>
<div id={styles.navBar}>
<a href="/" id={styles.navLink}>
<h3>Home</h3>
</a>
<a href="/about.js" id={styles.navLink}>
<h3>About</h3>
</a>
</div>
<div id={styles.content} className="content">
<br/>
<h2>Test Variable: {testVariable}</h2>
<button id={styles.incrementButton} onClick={setTestVariable(1)}>Set Test Variable to 1</button>
</div>
</div>
)
}
export default Home
When I tap the button the variable on my page changes to 1, however when I switch pages the variable goes back to 0. I also receive no errors of any sort. All feedback is greatly appreciated! Thanks for your time.
The variable goes back to 0 because you are using a tags wich "reloads" the page
To navigate you should use the Link component that is built in next.
This Link component prevents the default behavior of reload the page and you can keep your state while navigate on the page

Is not-returned code and content available for public in the source code?

I am new to nextjs and webdev. I been following a couple of tutorials but I still have a doubt.
Lets say I have this page:
// /pages/test/load.js
import Link from 'next/link'
import Layout from '../../components/layout'
import { useRouter } from "next/router";
export default function TestLoad() {
const router = useRouter();
const query = router.query;
console.log(query.info)
// I don't want to show any of this code here, or the return in case queri.info != true.
if (query.info == 'true') {
console.log('TRUE RETURN')
return (
<Layout>
<h1>First Post</h1>
<h2>
<Link href="/">
<a>Back to home</a>
</Link>
</h2>
<p>Some hidden content that I don't want to show to all the users.</p>
<p>Blah</p>
// Images and more stuff
</Layout>
)
} else {
return (
<>
<h1>Nothing to show</h1>
<Link href="/">
<a>Back to home</a>
</Link>
</>
)
}
}
If I deploy this code with Vercel for example, and try access to http://<some-url>/test/load?info=false is the code/info for query.info == 'true' available?
Of course in this particular case anyone can just try to access to http://<some-url>/test/load?info=true and view the content.
My idea is then try to wrap up the true/false logic into getStaticProps.
My ultimate idea is to render a different html code depending on the props passed by getStaticProps.
Thanks!

Next-JS Link tag not properly redirecting?

I'm trying to learn Next-JS, and I'm making a small, experimental project to familiarize myself. However, something seems to be going wrong with the Link tag. It does redirect to the friends page specified, but for some reason, the page content remains the same as it is on home. And clicking the test link while on this page, it attempts to redirect not to "#", but to "friends#".
Is there something I'm not understanding here? Any help would be appreciated. Here is my conde:
index.tsx:
import Link from 'next/link';
import styles from '../styles/Home.module.css'
const HomePage = () => {
// const handleClick = (e: Event) => {
// e.preventDefault()
// router.push('./friends')
// alert(router)
return (
<div className={styles.div}>
<h1 className={styles.h1}>WELCOME TO HOME PAGE</h1>
<ul className={styles.ul}>
<li className={styles.li}>
<Link href='/friends'>
<a>FRIENDS</a>
</Link>
</li>
<li className={styles.li}>
<Link href="#">
<a>TEST</a>
</Link>
</li>
</ul>
</div>
)
}
export default HomePage
friends.tsx:
import Link from 'next/link'
const Friends = () => {
return (
<div>
<h1>WELCOME TO FRIENDS</h1>
<Link href="/">
RETURN TO HOME
</Link>
</div>
)
}
export default Friends;
_app.tsx:
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import HomePage from '../pages/index'
function MyApp({ Component, pageProps }: AppProps) {
return <HomePage />
}
export default MyApp
Screentshots of what I'm seeing are listed below. Note the URLs.
Home page
Friends page
After clicking the Test link, for which the href is currently "#"
You're seeing the same page on all links is because you're always displaying the same component, which is <Homepage />. Instead, you need to render the component with pageProps to display the correct page according to the url.
The updated code of the _App component should be something like, where you need to display the Component available through the props.
import '../styles/globals.css'
import type { AppProps } from 'next/app'
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
export default MyApp
Please also take a look at the docs of the Next.js regarding the custom _app, to see available options/features out of the box.
From docs: The Component prop is the active page, so whenever you navigate between routes, Component will change to the new page. Therefore, any props you send to Component will be received by the page.
pageProps is an object with the initial props that were preloaded for your page by one of our data fetching methods, otherwise it's an empty object.

Expected server HTML to contain a matching <div> in <div>. Next JS error

I'm creating a whatsapp clone using next.js. On the first load of app i'm getting this error.
Warning: Expected server HTML to contain a matching <div> in <div>.
at div
at O (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1621219033615:37232:19450)
at div
at Paper (http://localhost:3000/_next/static/chunks/pages/chat.js?ts=1621219033615:45091:23)
at WithStyles (http://localhost:3000/_next/static/chunks/pages/chat.js?ts=1621219033615:64751:31)
at div
at Drawer (http://localhost:3000/_next/static/chunks/pages/chat.js?ts=1621219033615:33839:29)
at WithStyles (http://localhost:3000/_next/static/chunks/pages/chat.js?ts=1621219033615:64751:31)
at SideBar (http://localhost:3000/_next/static/chunks/pages/chat.js?ts=1621219033615:67329:75)
at div
at O (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1621219033615:37232:19450)
at Chat (http://localhost:3000/_next/static/chunks/pages/chat.js?ts=1621219033615:73282:70)
at SideMenuProvider (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1621219033615:25916:23)
at MyApp (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1621219033615:31532:24)
at ErrorBoundary (http://localhost:3000/_next/static/chunks/main.js?ts=1621219033615:726:47)
at ReactDevOverlay (http://localhost:3000/_next/static/chunks/main.js?ts=1621219033615:829:23)
at Container (http://localhost:3000/_next/static/chunks/main.js?ts=1621219033615:8388:5)
at AppContainer (http://localhost:3000/_next/static/chunks/main.js?ts=1621219033615:8876:24)
at Root (http://localhost:3000/_next/static/chunks/main.js?ts=1621219033615:9012:25)
I'm totally unaware of the source of this error. and they also did not specified from which file, the error is occurred.
Because of this error App UI gets shatter from this
Actual UI of APP
to this
Errored UI of APP
If anybody have any idea why this is happening please help me.
_document.js code here
import React from "react";
import Document, { Html, Main, NextScript, Head } from "next/document";
import { ServerStyleSheets } from "#material-ui/core/styles";
export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
{/* Meta Tags for SEO */}
<meta charSet="utf-8" />
<meta httpEquiv="X-UA-Comatible" content="IE=edge" />
<meta
name="description"
content="A WhatsApp clone made using next js and firebase."
/>
<meta name="keywords" content="WhatsApp Clone" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
MyDocument.getInitialProps = async (ctx) => {
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
You may need to import your components dynamically:
const MyDynamicComponent = dynamic(() => import('./myComponent'))

Importing FontAwesome icons by string array in React.js

I'm having an array of sidebar elements in my React.js project where each element is represented as object which among others has its own FontAwesome icon defined as string, like e.g. fa-phone. Now there's a problem with FontAwesome's integration into React.js; each icon has to be separately imported and added to the library, according to their manual.
import * as React from 'react';
import { library } from '#fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
interface SidebarElement {
fa: string,
href: string,
title: string
}
interface SidebarElements {
elements: SidebarElement[]
}
export default class Sidebar extends React.Component<SidebarElements, {}> {
render() {
const elements = this.props.elements.map((element, key) => {
// tweak icon name so it matches component name...?
...
// the two lines below obviously won't work
import { element.fa } from '#fortawesome/free-solid-svg-icons'
library.add(element.fa);
return (
<li key={key} className="nav-item">
<a className="nav-link" href={element.href}>
<FontAwesomeIcon icon={element.fa} />
<span>{element.title}</span>
</a>
</li>
);
})
return (
<ul className="sidebar navbar-nav">{elements}</ul>
);
}
}
But the solution above obviously won't work, since imports have to happen at top-level and won't take the component name from a variable. Are there any alternative ways to import icons without exactly knowing them from the beginning? Or am I forced to import all icons at the same point I'm defining my sidebar elements?
I went with this same issue on a personal project I'm building. The first problem I found was related to how dynamically rendering the icon from a query?
Main app container:
import React from "react"
import Header from "../components/header"
import Navigation from "../components/navigation"
import Footer from "../components/footer"
import containerStyles from "./styles.module.less"
import { library } from "#fortawesome/fontawesome-svg-core"
import { fab } from "#fortawesome/free-brands-svg-icons"
library.add(fab)
const IndexPage = ({ data }) => (
<div className={containerStyles.home}>
<div className={containerStyles.menu}>
<Header />
<Navigation />
</div>
<Footer />
</div>
)
export default IndexPage
Also, my icons are part of the free-brand version so imported them to the library.
So the first thing I did was to import the library and create a pair of null variables on my child component, one for the prefix and the other one for the icon itself:
In my project, I'm consuming the data from an API endpoint, the query I built to get the information is the following:
Theoretically, all was set just for mapping the array and render each item as we normally do:
<FontAwesomeIcon
icon={[
(faprefix = document.node.prefix),
(faicon = document.node.icon),
]}
size="lg"
/>
But the child component was rendering nothing. Why was this? Simply because of both document.node.prefix and document.node.icon are returning strings so when the component mapped the data from the array, it ended trying to render something like this:
<svg data-prefix="'fab'" data-icon="'github'" >
Please note the single quotation mark wrapping the string
My solution to this was to use a replace() method with a regex to remove the wrapping quotations marks:
<FontAwesomeIcon
icon={[
(faprefix = document.node.prefix.replace(/'/g, "")),
(faicon = document.node.icon.replace(/'/g, "")),
]}
size="lg"
/>
Child footer component
import React from "react"
import { StaticQuery, graphql } from "gatsby"
import containerStyles from "../pages/styles.module.less"
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome"
let faicon = null
let faprefix = null
const Navigation = ({ data }) => (
<StaticQuery
query={graphql`
query FooterTemplate {
allStrapiLink {
edges {
node {
id
icon
url
prefix
}
}
}
}
`}
render={data => (
<>
<footer>
<p>Freddy PolanĂ­a {new Date().getFullYear()}</p>
<div className={containerStyles.links}>
{data.allStrapiLink.edges.map(document => (
<div key={document.node.id}>
<a
href={document.node.url}
rel="noopener noreferrer"
target="_blank"
>
<FontAwesomeIcon
icon={[
(faprefix = document.node.prefix.replace(/'/g, "")),
(faicon = document.node.icon.replace(/'/g, "")),
]}
size="lg"
/>
</a>
</div>
))}
</div>
</footer>
</>
)}
/>
)
export default Navigation
Now my icons are rendering from the endpoint's data. I hope this could help to solve your issue.

Categories

Resources