Nextjs causing hard refresh when i click in the Link - reactjs

I have an app react using CRA, and I am trying to turn it into an SSR app using next. So, since there is little, the only things I changed were:
getInitialProps instead of useEffect and useState
Link from "next / link" instead of using react router dom
But when I click on the link, I get hard refresh.
Here is what is generating the link:
<Link href={post.meta.slug}>
<a>{post.title}</a>
</Link>;
I also tried with href={post.meta.slug} as={post.meta.slug}.
In my pages directory i have:
index.jsx
[slug].jsx
And this is how I get the post in [slug].jsx:
const PostPage = ({ post }) => {
return <Base>{post ? <Post post={post} /> : null}</Base>;
};
PostPage.propTypes = {
post: PropTypes.object,
};
PostPage.getInitialProps = async ({ query }) => {
const post = await getPostBySlug(query.slug);
return { post };
};
And so far I couldn't identify the error.
Here is the complete code: https://gitlab.com/flakesrc/blog-webstation-next
If you want to clone:
git clone https://gitlab.com/flakesrc/blog-webstation-next.git
cd blog-webstation-next
npm install
npm run dev

Have you tried this format for your Link?
<Link href='/[slug]' as={`/${post.meta.slug}`}>
<a>{post.title}</a>
</Link>
Here is a good example of this type of routing for dynamic pages as well as the section in the docs that also speaks to this a bit.

Related

How to programmatically close bootstrap 5 offcanvas in Next Js

I use using bootstrap 5 in my NextJs app my installing like this:
npm install bootstrap and imported the necessary files into my project via _app.js like this:
import 'bootstrap/dist/css/bootstrap.css'
...
useEffect(() => {
import("bootstrap/dist/js/bootstrap");
}, []);
Now the problem is that when I open the offcanvas modal and click a link from there, the offcanvas stays open after the page changes, instead of closing. How do I programmatically close the offcanvas when the pathname changes and only when the offcanvas is active. Thank you
NB: I am not using react-bootstrap
Import like this:-
useEffect(() => {
import("bootstrap/dist/js/bootstrap.bundle");
}, []);
Recently I ran into the same problem, but here is how I got things working.
First of I do not think you are importing bootstrap in to your nextjs app the right way, but what works work. However I think you should import bootstrap into your nextjs app this way.
useEffect(() => {
typeof document !== undefined
? require("bootstrap/dist/js/bootstrap")
: null;
}, []);
I don't want to make this too long, so lets dive straight into creating the solution. First you have to create a custom function to close the offCanvas when you click your link.
const topicRef = useRef(null);
const closeOffCanvas = (event) => {
event.stopPropagation();
const bootstrap = require("bootstrap/dist/js/bootstrap");
var myOffcanvas = topicRef.current;
var bsOffcanvas = bootstrap.Offcanvas.getInstance(myOffcanvas);
bsOffcanvas.hide();
};
Now create your link like so and call the function we just created
<Link href={`/article/18004/close-bootstrap-5-offcanvas-in-next-js`} passHref>
<a href="#" onClick={closeOffCanvas}>navigate to page then close offCanvas</a>
</Link>
I hope this helps you

React Native Navigation, open universal link from within own app

I am using React Navigation for React Native. I have successfully configured it to handle universal link, which is something like this
// linking.ts
import { APP_ID } from '#env';
const config = {
screens: {
LoginScreen: 'authorize',
RegisterScreen: 'register',
CustomerStack: {
screens: {
OrderDetailScreen: 'customer/order/:orderId',
},
},
},
};
const linking = {
prefixes: [`${APP_ID}://app/`, 'https://example.com/app/'],
config,
};
export default linking;
// App.tsx
import linking from './linking'
const App = () => {
return (
<NavigationContainer linking={linking}> <MyApp /> </NavigationContainer>
)
}
When I press a link in the browser such as https://example.com/app/customer/order/1234, then it successfully opens my app's order page.
Problem
I want to be able to open the url such as https://example.com/app/customer/order/1234 indside my app and have it open the order page. I have tried
<Button onPress={() => Linking.openURL('https://example.com/app/customer/order/1234')} />
but (testing on IOS) it switch to the web browser first to open the link, and then open my app.
Is it possible to open the order page directly inside my app without switching to the browser first.
Note: I am trying to implement an in-app notification history page, each notification item has the link saved in the database, and when the user clicks on the item I want to navigate the user to the page as configured in linking.ts. I know it is possible to parse the link and use navigation.navigate() instead, but that means I will have 2 places for the linking configuration. I think it would be great if I can reuse the existing logic provided by React Navigation.
In React Navigation, you can use the useLinkTo hook. This hook allows you to navigate inside the application using a path.
This will allow you to use the following option:
const linkTo = useLinkTo();
return (
<Button onPress={() => linkTo('/customer/order/1234')} />
);
If using a URL is mandatory, then you can use extractPathFromURL, an internal React Navigation function, to remove the prefix.
import extractPathFromURL from '#react-navigation/native/src/extractPathFromURL';
import linking from './linking'
// ...
const linkTo = useLinkTo();
return (
<Button onPress={() => {
const path = extractPathFromURL(linking.prefixes, 'https://example.com/app/customer/order/1234');
const pathWithSlash = path.startsWith('/') ? path : '/' + path;
linkTo(pathWithSlash);
} />
);
extractPathFromURL is not part of the official API and may be removed in future versions. For reliability, you can create a duplicate of this function in the project.

How to use useNavigate previous page without accidentally leaving my site

Problem: I'm trying to use useNavigate(-1) to go to previous page, but I just implement a way to people arrive in my SPA with a pasted link (or social media tho), so, when user tries to go back they leave my site xP.
Process: I could use useNavigate("/"), but this makes my page scroll to the top and I want to keep the previous users position, I rather not use a state with my scroll position, but if nothing else is possible or simple...
Context: is a restaurant menu ifood-like page, this problems occurs in product page and I want to go back to product list.
What I'm trying to achieve: some way to check if previous page is out of my domain and not go there, replacing with my root.
ps. I'm using "react-router-dom": "^6.0.0-beta.0"
In earlier versions of React Router DOM, there was a smarter goBack method, but in modern versions (including the v6 beta), you must use the fact that the location.key is "default" for the initial entry to the app's history.
I created an npm package that encapsulates this functionality in a simple React hook.
Install use-back
npm install use-back
Usage
import {useBack} from "use-back";
const BackButton = () => {
const {hasBack, handleBack} = useBack();
return (
<button type="button" onClick={handleBack}>
{hasBack ? "Go Back" : "Go Home"}
</button>
);
};
CodeSandbox Demo
Source Code
The source code is basically the following, which you can also include in your application.
const MagicalBack = () => {
const navigate = useNavigate();
const location = useLocation();
const hasPreviousState = location.key !== "default";
const handleClick = () => {
if (hasPreviousState) {
navigate(-1);
} else {
navigate("/");
}
};
return (
<button type="button" onClick={handleClick}>
{hasPreviousState ? "Go Back" : "Go Home"}
</button>
);
};
CodeSandbox Demo

Next JS build failing due to ''Export encountered errors on following paths"

I am encountering this build error when I deploy my next js site to vercel
15:02:38 > Build error occurred
15:02:38 Error: Export encountered errors on following paths:
15:02:38 /about/undefined
15:02:38 at exportApp (/vercel/workpath0/node_modules/next/dist/export/index.js:30:1103)
15:02:38 at processTicksAndRejections (internal/process/task_queues.js:97:5)
15:02:38 at async /vercel/workpath0/node_modules/next/dist/build/index.js:39:69
15:02:38 at async /vercel/workpath0/node_modules/next/dist/build/tracer.js:1:525
My website does not have a page called about, so I really don't know what this error is referring to. I checked as much as I could to find a reliable answer, but couldn't. Any help is appreciated!
I ran into the same error a few days back.
A few Points I would want to clarify:
Build happens as per the pages and URLs of the browser.
If you are seeing something like
Export encountered errors on following paths:
/about
Then you should copy "/about" and paste it in front of the current URL like:
http://localhost:3000/about
Then you will see the error or any major warning.
Resolve it, you are ready to Build.
Adding fallback like this worked for me:
{fallback: false}
I got this error too. I found out that I was using a component without props.
Removing it or passing props will fix the issue
The problem was resolved by outputting the following log.
All pages have been logged.
export async function getStaticProps({ params: {slug} }) {
// ↓add
console.log(`Building slug: ${slug}`)
}
In my case I just install the "eslint" by adding this to my script "lint": "next lint" then I just run the script by typing "npm run lint" then I select "recommended", after installation I run the script again by typing "npm run lint", this time it gave me an error saying that I should downgrade the "eslint" to "version 7" the good thing is they already gave the "npm command" that I should type to downgrade the version, just follow the description. then run the "eslint" again until "you don't see any errors in the installation".
then run the "npm run build" this will give you a definitive description of all the errors in the code and why the "build process is failed"
based on the errors. I fixed the following.
all exported components in my pages folder should be like this:
const Home = () => {
return()
}
export default Home
not this:
const home = () => {
return()
}
export default home
I have unused import { useState } from 'react' so I remove it.
I have a prop on the about page that I never use.
const About = ( { listings } ) => {
return()
}
and I use the getStaticProps() to call data from pages/api/mydata so I change it to getServerSideProps()
then I try the "npm run build" again, this time it gave me no errors and the build process was successful.
If your fallback is set to true you need to handle the fallback state.
You need to add
if (router.isFallback) {
<h1>Data is loading</h1>;
}
You can put anything in the body of if statement, and this is going inside the function component like this
const PostDetails = ({ post }) => {
const router = useRouter();
if (router.isFallback) {
<h1>Data is loading</h1>;
}
return (
)
In my case I discovered that the error occurred because I was mapping through an array and I needed to check if the array existed or not first.
using the && i.e. logical and assignment operator like this data && data.map()=> solved it for me
Add fallback on your page component like this:
const {
isFallback,
} = useRouter();
if (isFallback) {
return <h1>Fallback</h1>;
}
I discovered that there was an object without a property that was needed to generate the page. Once I removed it, all was well
I had the following error because I declared my functional component like this by mistake.
async functionName() {
...
}
export default functionName
I changed it to
const FunctionName = () => {
...
}
export default FunctionName
I hope it would also work for someone who faces the same error.
My problem was that route, while it existed, had an error in it that wasn't being shown. In my case where I have MDX files that are being used with next.js to generate routes, one of my MDX files had a property/value that was invalid.
For example, if you have the following in an MDX:
---
slug: test-mdx-post
title: This is a test post
date: 2020-06-10
topics:
- stackoverflow
---
If stackoverflow is not a valid topic assigned elsewhere, you'll get the error Export encountered errors on following paths: on your /blog/test-mdx-post or whatever your equivalent route is for that MDX.
In short, check that your MDX files are spell-checked and all properties and values are usable.
In my case this happened when backend API was down. So you need to wait till backend API will reset and try again
After Deleting node modules and installing them again, by running yarn install, error got resolved.
I got this error, I found out that I unintentionally imported useEffect dependency like this
import { useEffect } from 'react/cjs/react.development';
Once, I updated it and imported it from 'react'.
import React, { useEffect } from 'react';
I am able to build the project.
In my case, I am not using the props when declaring them with component and then using them in that conponent like this
Like in Home Component, declaring the props with component
const Home = () => {
....
....
return (
...
<MyComponent prop1={prop1} prop2={prop2} />
...
)
};
and then using it like this in MyComponent in different way
const MyComponent = ({prop1,prop2}) => {
};
Hence causing problem.
Soln
So I removed props2 from Home as it is a very small function and defined the function in MyComponent only.
This happened to me because I had a /components folder inside my /pages folder. Because I'm new to Next.js, I didn't realize even folders in the /pages folder become URLs, so my /components/Navbar.js file was actually reachable via localhost:3000/components/Navbar. Because Navbar took props, those props weren't being passed properly because... well, how could they be? Next.js was trying to build that Navbar page and couldn't, so threw the error.
The fix was just to move /components out to the root directory.
You should run your project on 'dev' mode, then go to url -from mentioned in terminal- on browser. You will see "more detailed error" on console or terminal
Just in case someone is still facing this issue:
I solved this by moving a file (that was a class and wasn't rendering a page) under the 'pages' folder to a different folder.
I had a file 'ErrorBoundary' with a class that was used to wrap my application, and it was under the 'pages' folder but it was not rendering a page (it was a class) - so I moved it to a different folder and it fixed this issue.
References:
https://nextjs.org/docs/advanced-features/error-handling
In my case the slug was react, so the path was category/react
The problem was resolved when deleting this slug.
In my case I forgot to export getStaticProps function
// this is wrong
const getStaticProps: GetStaticProps = async () => {...}
// should be exported
export const getStaticProps: GetStaticProps = async () => {...}
I do not know what exactly caused my own problem, but I encountered a build failure due to this error - "Error: Export encountered errors on following paths:..."
I was able to resolve the issue by performing a project restructuring - I moved all my page components (all the component folders containing all the components) out of the pages folder.
It seems NextJs will only accept pages in the pages folder. So ensure to structure your project using other method(s) that leaves all your components outside.
Okay, let me tell you what's causing the problem,
I was using a JS file as a component like I was using
<Hero/> and <Hero> component is receiving props from index.js file in my directory
// Code Sample
export default function Home({ posts }) {
return (
<>
<Hero posts={posts}/>
</>
)
}
Like this, it works fine, but when I run my file independently (going to /Hero), it gives me an error. So fixing this will not return any error anymore!!
I have the issue with my blitzjs application, the errors:
> Build error occurred
Error: Export encountered errors on following paths:
/products/new
.....at async Span.traceAsyncFn
....
So wrapping the async call inside a Suspense will fix the issue.
before:
// products/new.ts
const NewProductPage = ()=> {
const currentUser = useCurrentUser(); // async call
return <div>{currentUser.displayName}</div>;
}
after:
// products/new.ts
const DisplayUserName = ()=>{
const currentUser = useCurrentUser(); // async call
return <div>{currentUser.displayName}</div>;
}
const NewProductPage = ()=> {
return (
<Suspense fallback={<div>Loading...</div>}>
<DisplayUserName />
</Suspense>);
}
blitzjs
blitzjs-tutorial
This happened to me, but error was not undefined, rather it was caused by a .DS_Store file.
Error: Export encountered errors on following paths:
/articles/[id]: /en/articles/.DS_Store
Lesson learned there, be careful what you put in your getStaticPaths function! Check your getStaticPaths to see which paths you are adding!
export const getStaticPaths = () => {
// Log what you are actually are returning in this function to see
// if you are sending a param which is undefined, .DS_Store or
// something other you shouldn't send.
}
What I did, was to read the content of a folder. One day, a wild .DS_Store file appeared in that folder and I returned it as params in getStaticPath :)
In my case there was a low internet connection when I reconnected to the internet and build it again, it was built successfully.
for me I forget to connect my devies to internet and that is why i was geting this error

nextjs Dynamic route rendering content not working

I am stuck on this problem for many days. I am using Next.js and have 3 pages.
pages/index.js
pages/categories.js
pages/categories/[slug].js
The categories/[slug].js is using Next.js fetching method name getServerSideProps that runs on each request and used for build dynamic pages on runtime. The categories/[slug].js is rendering a dynamic content on the page that dynamic content comes from the CMS as a response from the API Endpoint. Dynamic content is nothing but a string that contains HTML with <script /> elements.
Note: To fetch the content from the CMS we have to send a POST request with the CMS credentials like username, password, and the page slug for the content. I am using axios library to send a post request and the method is inside post.js file.
post.js:
import axios from 'axios';
const postCMS = async (slug) => {
const url = `${process.env.CMS_API_URL}/render-page/`;
let pageSlug = slug;
// If the pageSlug is not start with `/`, then create the slug with `/`
if (!pageSlug.startsWith('/')) {
pageSlug = `/${pageSlug}`;
}
const head = {
Accept: 'application/json',
'Content-Type': 'application/json'
};
const data = JSON.stringify({
username: process.env.CMS_API_USERNAME,
password: process.env.CMS_API_PASSWORD,
slug: pageSlug
});
try {
const response = await axios.post(url, data, {
headers: head
});
return response.data;
} catch (e) {
return e;
}
};
export default postCMS;
But for the rendering content on the categories/[slug].js page, I am using the Reactjs prop name dangerouslySetInnerHTML to render all the HTML which also contains <script /> elements in the JSON string.
pages/categories/[slug].js:
<div dangerouslySetInnerHTML={{ __html: result.html }} />
The content is loading fine based on each slug. But when I navigate to that category page i.e.pages/categories/index.js.
<Link href="/categories/[slug]" as="/categories/online-cloud-storage">
<a>Online Cloud Storage</a>
</Link>
It has a <Link /> element and when I click it.
The dynamic content is loading fine but that dynamic content contains accordion and slider elements they are not working. I think <script /> of these elements is not working. But when I refresh the page they work fine. See this.
They also work fine when I set the Link something like this.
<Link href="/categories/online-cloud-storage" as="/categories/online-cloud-storage">
<a>Online Cloud Storage</a>
</Link>
But after setting the link like the above method, the click is caused to hard reload the page. But I don't want this. Everything should work. When the user clicks on the category link.
Is there a way to fix this?
Why the content elements are not working when you click from the categories/index.js page?
Github repo
Code:
pages/index.js:
import React from 'react';
import Link from 'next/link';
const IndexPage = () => {
return (
<div>
<Link href="/categories">
<a>Categories</a>
</Link>
</div>
);
};
export default IndexPage;
pages/categories/index.js:
import React from 'react';
import Link from 'next/link';
const Categories = () => {
return (
<div>
<Link href="/categories/[slug]" as="/categories/online-cloud-storage">
<a>Online Cloud Storage</a>
</Link>
</div>
);
};
export default Categories;
pages/categories/[slug].js:
import React from 'react';
import Head from 'next/head';
import postCMS from '../../post';
const CategoryPage = ({ result }) => {
return (
<>
<Head>
{result && <link href={result.assets.stylesheets} rel="stylesheet" />}
</Head>
<div>
<h1>Category page CMS Content</h1>
<div dangerouslySetInnerHTML={{ __html: result.html }} />
</div>
</>
);
};
export const getServerSideProps = async (context) => {
const categorySlug = context.query.slug;
const result = await postCMS(categorySlug);
return {
props: {
result
}
};
};
export default CategoryPage;
The problem here is that <script> tags which are dynamically inserted with dangerouslySetInnerHTML or innerHTML, are not executed as HTML 5 specification states:
script elements inserted using innerHTML do not execute when they are inserted.
If you want to insert new <script> tag after the page has initially rendered, you need to do it through JavaScript's document.createElement('script') interface and appended to the DOM with element.appendChild() to make sure they're executed.
The reason why the scripts don't work after changing routes, but they do work after you refresh the page is tied to Next.js application lifecycle process.
If you refresh the page, Next.js pre-renders the entire website on the server and sends it back to the client as a whole. Therefore, the website is parsed as a regular static page and the <script> tags are executed as they normally would.
If you change routes, Next.js does not refresh the entire website/application, but only the portion of it which has changed. In other words, only the page component is fetched and it is dynamically inserted into existing layout replacing previous page. Therefore, the <script> tags are not executed.
Easy solution
Let some existing library handle the hard work for you by parsing the HTML string and recreating the DOM tree structure. Here's how it could look in jQuery:
import { useEffect, useRef } from 'react';
import $ from 'jquery';
const CategoryPage = ({ result }) => {
const element = useRef();
useEffect(() => {
$(element.current).html($(result.html));
}, []);
return (
<>
<Head>
{result && <link href={result.assets.stylesheets} rel="stylesheet" />}
</Head>
<div>
<h1>Category page CMS Content</h1>
<div ref={element}></div>
</div>
</>
);
};
export const getServerSideProps = async (context) => {
/* ... */
return { props: { result } };
}
Harder solution
You would have to find a way to extract all <script> tags from your HTML string and add them separately to your page. The cleanest way would be to modify the API response to deliver static HTML and dynamic script in two separate strings. Then, you could insert the HTML with dangerouslySetInnerHTML and add script in JS:
const scripts = extractScriptTags(result.html); // The hard part
scripts.forEach((script) => {
const scriptTag = document.createElement('script');
scriptTag.innerText = script;
element.current.appendChild(scriptTag);
});
IMHO, I believe that the script non-proper loading is due to erroneous import of the scripts on Client-Side Rendering (CSR) and Server-Side Rendering (SSR). Read more here, but also take a look on this interesting article. This would also explain the problematic behavior of your link component.
In your case, I understand that this behavior is due to false handling of the lifecycle of the page and its components during CSR, as many scripts might need to properly shared across the navigation of pages, possibly in SSR. I do not have the full picture of what the problem is or extended expertise on NextJS, but I believe that those scripts should be imported in one place and possibly rendered on the server, instead of false importing on each page, falsely letting CSR do the work in a non-NextJS optimized manner.
The suggested way is to use a custom Document implementation (SSR-only) for your application, where you can define the scripts. See here for more details on this. Also I suppose you already have setup a custom App file for your App, where you will use it in your document, for both CSR and SSR rendering common to all pages (See this SO question for more on that).
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
// ...
}
render() {
return (
<Html>
<Head>
{/*Your head scripts here*/}
</Head>
<body>
<Main />
{/*Your body scripts here*/}
<NextScript />
</body>
</Html>
)
}
}
The Head native component does a lot of work on the background in order to setup things for scripts, markup, etc. I suggest you go that way, instead of just adding the scripts into each page directly.

Resources