Typical differences between routers and links in NextJS - reactjs

I am new to NextJS. I would like to know the typical variations and use cases between next/router and next/link.
Which one should I use in various scenarios? Which does what? For instance, if I want to load shared components or navigate among pages which are server-side rendered. Also what is the difference if I call a page with a normal 'a' tag or use a link/router to achieve the same.
import { useRouter } from 'next/router'
function ActiveLink({ children, href }) {
const router = useRouter()
const style = {
marginRight: 10,
color: router.pathname === href ? 'red' : 'black',
}
const handleClick = (e) => {
e.preventDefault()
router.push(href)
}
return (
<a href={href} onClick={handleClick} style={style}>
{children}
</a>
)
}
export default ActiveLink
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
<li>
<Link href="/about">
<a>About Us</a>
</Link>
</li>
</ul>
)
}
export default Home

what is the difference if I call a page with a normal 'a' tag or use a link/router to achieve the same?
The main difference between a normal <a> tag and next/link or next/router is that the latter two are used for client-side transitions. So a normal <a> tag will load/navigate to the page with an http request, while router and link with load the page client-side.
You can use next/router (inside a function in your component) when next/link is not enough, for example if you need to do some operations before render the new component.
So both router and link has similar behaviours, you can choose, based on what your app needs to do, which one to use.
Both, will run data fetching methods (getServerSideProps, getStaticProps, and getInitialProps)

Link is just an abstraction for the router, it is a lot easier to use the Link component than to manually construct the link every time.

Related

NextJs 13 Experimental App Dir Hash in routes not directing to the hash id

I am using Nextjs 13 with the experimental App Dir but am not sure if this problem I am facing has anything to do with the issue I am facing. I have an id in my home page of "faqs" and when I click on the link, I can see it successfully goes to that link but does nothing in the browser. If I am on another page, I click the link and it takes me to the home page with the correct url but still stays on the top of the page and does not scroll to the indicated id. I did implement scroll={false} as suggested in the documentation but it makes no difference.
Here is a snippet of the relevant code parts:
"use client"
import React, { useState } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link"
const Navigation = () => {
const router = useRouter()
...
In the return:
<Link scroll={false} href="/#faqs">FAQS</Link>
I Even tried:
<button type="button" onClick={() => router.push("/#faqs")}>FAQS</button>
In React the hash works fairly well but in next js, even only in client rendering it seems convoluted. If anyone knows what I am doing wrong or if there is a viable work around, I would sure appreciate it.
Thank you in advance.
If I am missing anything, please let me know.
I use hashtags a lot and I plan to start using the app directory in future projects, so I dug into this and it's not pretty. Apparently, NextJS uses a different package for app directory components client-side called "next/navigation". It's very different from "next/router". Also, when using "next/link" elements, NextJS does not trigger the onRouteChangeComplete event when location.hash changes but location.pathname does not.
So, in order to detect a hash change and scroll to the associated element, I finally had to implement this hack:
"use client"
import { Inter } from '#next/font/google'
import paragraph from './paragraph'
import Link from 'next/link'
import { useEffect, useState } from 'react'
const inter = Inter({ subsets: ['latin'] })
export default function Home() {
const [navClick, setNavClick] = useState(false);
useEffect(() => {
setTimeout(() => {
const hash = window.location.hash;
if (hash) document.querySelector(hash).scrollIntoView();
}, 0);
}, [navClick])
const toggleNavClick = () => setNavClick((oldVal) => !oldVal);
return (
<main>
<nav>
<ul>
<li>
<Link href="/#one" onClick={toggleNavClick}>Section One</Link>
</li>
<li>
<Link href="/#two" onClick={toggleNavClick}>Section Two</Link>
</li>
<li>
<Link href="/#three" onClick={toggleNavClick}>Section Three</Link>
</li>
</ul>
</nav>
<div className="container">
<section id="one">
<h1>Section One</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
<section id="two">
<h1>Section Two</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
<section id="three">
<h1>Section Three</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
</div>
</main>
)
}
Since the hash change cannot be detected because no event is triggered, I basically created an event by toggling navClick each time a link is clicked. The navigation logic is enclosed in setTimeout() function because it triggers after window.location is updated.
Repo: https://github.com/designly1/next-hash-test
Demo: https://next-hash-test.vercel.app/

Change parent component variable from child component when using routing in react

I am trying to update parent components title from child components url. But the child components are loaded as navigation
Here is my Sandbox
https://codesandbox.io/s/react-typescript-forked-z1ijxi
Here is my layout.tsx
import { createContext, useContext, useState } from "react";
import { Outlet, Link } from "react-router-dom";
const Layout = () => {
const title = "";
return (
<>
<h2>{title}</h2>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/blogs">Blogs</Link>
</li>
<li>
<Link to="/contact">Contact</Link>
</li>
</ul>
</nav>
<Outlet />
</>
);
};
export default Layout;
Here I need to change the {title} when each of the child component loads
I tried to follow this answer . But I was not able to accomplish that.. Sorry very new in React
I would recommend using React Redux in your project. This library adds a variable namespace that is available to every component. From this global namespace, you could define a title variable that is accessible to the Layout component and editable by any other component no matter where it is.
Redux takes some time to learn, but it's very useful in all kinds of projects. This article has more information on the concept of Redux, though the exact implementation is a little outdated.

How do I redirect to an External Link in react?

I am building a gallery where you click on the image and it will load in a separate component using props, this image is a URL, taken from an array, where the src property is loaded as a background image via CSS. My challenge is connecting the src data to the child component. See original question
I have found a solution to pass the data using the Link component. Now the URL string is being read like this: http://localhost:3000/https://photos.smugmug.com/photos....
As you can see there is an address within the address in the string.
I have tried changing the state of the URL string but did not work.
My question, how do I write a redirect to fix the HTTP address removing the localhost address
UPDATE
Many thanks to Taylor, Drew, and Ajeet for all of your help!
The solution is posted below, the main issue was I needed a function in the Image component to connect the src props from the GalleryContainer component.
I also changed all "a tags" to "Link components" to keep consistency. More details are in the explained solutions from Drew and Taylor, and also Ajeet code box here
Issues
I don't know why but you don't seem to use Link components consistently in your app; when using anchor (<a>) tags these types of links will reload the page and your app. A similar issue occurs when you manually set the window.location.href.
The Image wasn't correctly accessing the passed route state.
Solution
App
Reorder your routes from more specific to least specific, and remove the link from within the Switch component, only Route and Redirect components are valid children.
function App(props) {
return (
<>
<Router>
<Switch>
<Route path="/gallery" component={GalleryList} />
<Route path="/image" component={Image} />
<Route path="/" component={Home} />
</Switch>
</Router>
</>
);
}
Home
Use Link component to enter the gallery.
import { Link } from "react-router-dom";
...
<Link to="/gallery">
<h4>Click Here to Enter Gallery!</h4>
</Link>
GallerayList
Use Link component for the link back home.
import { Link } from "react-router-dom";
...
<Link to="/">Home</Link>
GalleryContainer
Refer to image source consistently, i.e. src. Pass along also the image id in route state, using a Link.
const GalleryConatiner = (props) => {
return (
// generates the gallery list!
<ul>
<li className={styles["gallery-list"]}>
<Link
to={{ pathname: "/image", state: { id: props.id, src: props.src } }}
>
<div
className={styles["div-gallery"]}
style={{
backgroundImage: `url(${props.src})`,
height: 250,
backgroundSize: "cover"
}}
></div>
</Link>
</li>
</ul>
);
};
src/Public/Image
Use a Link for the link back to the gallery. Use the useLocation hook to access the passed route state.
import { Link, useLocation } from 'react-router-dom';
const Image = (props) => {
const { state: { id, src } = {} } = useLocation();
return (
<section>
<h1 className={styles["h1-wrapper"]}>Image :{id}</h1>
<div className={styles.wrapper}>
<Link to="/gallery">BACK TO GALLERY</Link>
<ImageContainer id={id} key={id} src={src} />
</div>
</section>
);
};
src/Public/ImageContainer
It isn't clear what your plan is for this component and clicking on the div rendering the passed image as a background so just remove the window.location.href logic with history.push if you want to navigate elsewhere. You can access the history object via the useHistory React hook.
Demo
The disconnect is between the GalleryContainer and Image components. In order to access data from the <Link to=...> within the next component, you need to use props.location.propertyName.
So for example, your GalleryContainer needs to link like this:
<Link to={{ pathname: "/image", src: props.src }}>
And then the value can be retrieved inside the Image component like so:
<ImageContainer id={props.id} key={props.id} src={props.location.src} />
You can use
<Link to={{ pathname: "/image", state: { url: props.src } }}>
but then you would have to access it in the linked component like this: props.location.state.url
From there, you can use an <a> tag with an href to link to the src property.
You can simply use a tag to redirect.
<a target='_blank' href={}>
Link
</a>
Remove target attribute if you dont need to open in new tab.

Access the route in different way in next js

I have an application in nextjs. There i built the next route:
<Link
href="colors/[type]"
as={`colors/${type}`}
>
<a>click</a>
</Link>
This route redirect me on /colors/red, or colors/blue and so on, depending by the user. type here is a varible = dynamic element.
Also i have other link:
<Link href="colors">
<a>click to colors</a>
</Link>
This route should redirect me on /colors, but when i click i get error, because the first url colors/red is not equal with this type: colors. So i have to put something after colors to make things happen.
How to solve the issue? and how to make my first route to accept the last parameter as optional.?
The best way to do this is to use dynamic route (more). Start by creating a new page in your /colors folder named [[...type]].js that will catch either /colors or /colors/red. This setup will also catch route like /colors/red/blue.
You can retrieve the variable part of the url by using the next Router (see example below).
import React from 'react';
import Link from "next/link";
import {useRouter} from "next/router";
export default () => {
const router = useRouter()
const { type } = router.query;
return (
<div>
<Link href={'/'}>
Home
</Link>
<Link href={'/colors/red'}>
Red
</Link>
<Link href={'/colors/blue'}>
Blue
</Link>
<div>
{type ? (
`Type: ${type}`
) : (
'no type'
)}
</div>
</div>
);
}

Can I use href tag in reactjs?

I want to redirect a page in reactjs and for that I want to use href tag can I do that?
Here is the code for reference:
import React, { Component } from 'react';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
// import DoctorImg from './doctor.jpg';
class App extends Component {
render() {
return (
<Router>
<Link to = 'https://google.com/'><button>GO GOOGLE</button></Link>
</Router>
);
}
}
export default App;
You can use the Link tag available in React, internally every Link tag is converted to a anchor tag
import { Link } from 'react-router-dom';
<Link to="/Path" > Contact us </Link>
If you want to link to a webpage outside of your React app, a HTML anchor tag will work great:
Click here
target="_blank" will open the webpage in a new tab. rel="noopener noreferrer" prevents security risks, more detail here
If you want to link to a page within your React app you probably DON'T want to use the tag because this will cause your whole app to be reloaded, losing the app's current state and causing a delay for the user.
In this case you may want to use a package like react-router which can move users around your app without reloading it. See Rijul's answer for that solution.
You can use a simple href if you want a simple link:
<a href={'http://google.com'}>Google</a>
//or if you want to use or own
//import {yourcomponent} from'your dir'
import { Link } from 'react-router-dom';
<Link to="/Path" exact component={yourCompponent} > Contact us </Link>
<!-- if u want to use html -->
clik here
No, You are not supposed to. href will refresh the current page and open a new one. Technically href will refresh and push one route in history obj. In react i really suggest you to use react-router for routing
import { Link } from 'react-router-dom';
<Link to = 'https://google.com/'><button>GO GOOGLE</button></Link>
Do like this in react with react router
The answer is yes. You can.
<Router>
<button>GO GOOGLE</button>
</Router>
As for "should I" that is another question but for what you have asked the simple answer is yes and it will work fine.
<Link to={{ pathname: "https://twitter.com/Turkcell" }}
target="_blank"><i className="icon icon-twitter"/>
</Link>
You can use bro.

Resources