My next.js code for dynamic content and routing isn't working as expected - javascript

I'm following a YouTube tutorial for next.js and i got stuck on a very beginning point. I'm trying to create a dynamic content and routing for my website with the codes below but there is an error:
import Link from 'next/Link';
const people = [
{v: 'car', name: 'bruno'},
{v: 'bike', name: 'john'},
{v: 'airplane', name: 'mick'}
]
export default function Details (){
return <div>
{people.map( e => (
<div>
<Link as={'/${e.v}/${e.name}'} href="/[vehicle]/[person]">
<a>Navigate to {e.name}'s {e.v}</a>
</Link>
</div>
))}
</div>
}
The dynamic routing for the code above is working fine actually. But when i go to my details page, the content is looking like "${e.name}'s ${e.v}" this. It doesn't look like according to my parameters. I believe the problem occurs because of this line:
<Link as={'/${e.v}/${e.name}'} href="/[vehicle]/[person]">
I tried to change " ' " this quotation mark to this " ´ " back quote mark but that also doesn't work. Could you please help me?
I also get this error in my Developer Tools console. I changed the fb.me link for a purpose.
Warning: Each child in a list should have a unique "key" prop.
Check the top-level render call using <div>. See blablafb.me/react-warning-keys for more information.
in Link (at Details.js:13)
in Details
in App
in Unknown
in Context.Provider
in Context.Provider
in Context.Provider
in Context.Provider
in AppContainer

In your /pages/index.js file you change single quotes ' with back ticks ` to permit string interpolation, and add a key attribute to the top-level div where you map over people's array:
import Link from "next/link";
const people = [
{ v: "car", name: "bruno" },
{ v: "bike", name: "john" },
{ v: "airplane", name: "mick" }
];
export default function Details() {
return (
<div>
{people.map((e, idx) => (
<div key={idx}>
<Link href="/[vehicle]/[person]" as={`/${e.v}/${e.name}`} passHref>
<a>
Navigate to {e.name}'s {e.v}
</a>
</Link>
</div>
))}
</div>
);
}
Then create /pages/[vehicle]/[person].js where you display different content based on the link you clicked on:
import { useRouter } from "next/router";
import Link from "next/link";
export default function Person() {
const router = useRouter();
const { vehicle, person } = router.query;
return (
<div>
<p>
Vehicle: {vehicle}, Person: {person}
</p>
Go back{" "}
<Link href="/" passHref>
<a>Home</a>
</Link>
</div>
);
}

Related

How to use images in the array in the react?

I am creating a website. I am a beginner. I have an issue. I have an array of react components. I don’t know can I use React components as the array elements. They are images, imported from the folder of my project. Also, I have an array of names of news companies. The idea is to create blocks with the name and image above. I want to create blocks according to the my images array length. So if the length of this array is 4, the cards I have 4. The issue is I can't display images, I imported them to my project. Main code is in the main page component. Also, I have a component called Author Card. In it, I have a React component, that receives name and image as the props and put them in the card Html block.
Here is my main page component code:
import React from 'react';
import AuthorCard from "./MainPageComponents/AuthorCard";
import BBC_Logo from '../assets/images/BBC_Logo.png';
import FOX_Logo from '../assets/images/FOX_Logo.png';
import CNN_Logo from '../assets/images/CNN_logo.png';
import ForbesLogo from '../assets/images/forbes-logo.png';
function MainPage(props) {
const channels = [
{
name: 'BBC',
index: 1
},
{
name: 'FOX',
index: 2
},
{
name: 'CNN',
index: 3
},
{
name: 'FORBES',
index: 4
},
];
const logos = [
<BBC_Logo key={1} />,
<FOX_Logo key={2}/>,
<CNN_Logo key={3}/>,
<ForbesLogo key={4}/>
];
return (
<div className="main-page">
<div className="main-page_container">
<section className="main-page_channels">
{channels.map( (channel) => {
logos.map( (logo) => {
return <AuthorCard name={channel.name} img={logo} />
})
})}
</section>
</div>
</div>
);
}
export default MainPage;
Here is my Author Card component code:
import React from 'react';
function AuthorCard(props) {
return (
<div className="author-card">
<div className="author-img">
{props.img}
</div>
<div className="author-name">
{props.name}
</div>
</div>
);
}
export default AuthorCard;
Please, help!
I would handle this a bit differently. First thing the way you import your logos is not imported as a component. Rather you get the path/src of the image which you can then use in a component. Read more about that here: https://create-react-app.dev/docs/adding-images-fonts-and-files/
So the way I would do this is to put the logo img src into your channels array and then pass that img src to the AuthorCard component. Then in the AuthorCard component your use a component to render the image. Like this:
import React from "react";
import BBC_Logo from "../assets/images/BBC_Logo.png";
import FOX_Logo from "../assets/images/FOX_Logo.png";
import CNN_Logo from "../assets/images/CNN_logo.png";
import ForbesLogo from "../assets/images/forbes-logo.png";
export default function App() {
return (
<div className="App">
<MainPage />
</div>
);
}
const channels = [
{
name: "BBC",
index: 1,
img: BBC_Logo
},
{
name: "FOX",
index: 2,
img: FOX_Logo
},
{
name: "CNN",
index: 3,
img: CNN_Logo
},
{
name: "FORBES",
index: 4,
img: ForbesLogo
}
];
function MainPage(props) {
return (
<div className="main-page">
<div className="main-page_container">
<section className="main-page_channels">
{channels.map((channel) => {
return <AuthorCard name={channel.name} img={channel.img} />;
})}
</section>
</div>
</div>
);
}
function AuthorCard(props) {
return (
<div className="author-card">
<div className="author-img">
<img src={props.img} alt="author card" />
</div>
<div className="author-name">{props.name}</div>
</div>
);
}
Here, we are using the map function to iterate over the channels array and render an AuthorCard component for each channel. We pass the name property to the AuthorCard component, as well as the corresponding logo from the logos array.
Note that we are also passing a key prop to the AuthorCard component to help React identify each component uniquely. In this case, we're using the index property of each channel object.

Why does my TOC component add `{"-" + 1}` to its links every time I click on one of them?

I implemented a table-of-contents component to my site following this tutorial.
It works, but every time I click on one of the TOC's links, all the links (including the clicked one) in the TOC get a {"-" + 1} appended. So if I click a link, all those links go from #first-heading, #second-heading, etc. to #first-heading-1, #second-heading-1, etc. If I click one of the links again, they will all get #first-heading-2, #second-heading-2 etc., and so on. This behavior is of course problematic, as it breaks the links.
What's causing this? How do I fix it?
I noticed the tutorial uses the remark-slug plugin for the headings, while I use the gatsby-autolink-headers plugin. Can that be the source of the problem? I've not been able to test with the former, as I get an error when trying to install it.
EDIT: I've tried with both plugins. Same problem.
TableOfContents.js
import React from "react"
import Slugger from "github-slugger"
import { Link } from "gatsby"
const slugger = new Slugger()
export default ({ headings }) => (
<div className="table-of-contents">
<h3>On this page</h3>
<ol>
{headings
.filter(heading => heading.depth !== 1)
.map(heading => (
<li key={heading.value}>
<Link
to={"#" + slugger.slug(heading.value)}
>
{heading.value}
</Link>
</li>
))}
</ol>
</div>
)
post-template.js
import * as React from "react"
import { graphql } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import Layout from "../components/layout.js"
import Seo from "../components/seo.js"
const PostTemplate = ({ data, location }) => {
let post = data.mdx
return (
<Layout location={location}>
<Seo
title={post.frontmatter.title}
description={post.frontmatter.lead}
date={post.frontmatter.computerDate}
/>
<article className="article">
<h1 itemprop="headline">{post.frontmatter.title}</h1>
<p
className="lead"
itemprop="introduction"
>
{post.frontmatter.lead}
</p>
<MDXRenderer headings={post.headings}>
{post.body}
</MDXRenderer>
</article>
</Layout>
)
}
export default PostTemplate
export const pageQuery = graphql`
query PostBySlug($id: String!) {
site {
siteMetadata {
title
}
}
mdx(id: {eq: $id}) {
id
excerpt(pruneLength: 160)
body
frontmatter {
title
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "DD MMMM YYYY")
lead
}
headings {
depth
value
}
}
}
`
index.mdx
---
/* frontmatter */
---
<!-- component imported as shortcode in `layout.js` -->
<TableOfContents headings={props.headings} />
layout.js (excerpt)
import TableOfContents from "./article-components/TableOfContents"
const shortcodes = {
TableOfContents
}
export default function Layout({ children }) {
return (
<div className="layout-wrapper">
<Header />
<main>
<MDXProvider components={shortcodes}>
{children}
</MDXProvider>
</main>
</div>
)
}
It's because of the slugger. In their docs:
slugger.slug('foo')
// returns 'foo'
slugger.slug('foo')
// returns 'foo-1'
slugger.slug('bar')
// returns 'bar'
slugger.slug('foo')
// returns 'foo-2'
Because it ensures that the links are unique (like GitHub does), it appends the -1, -2, etc.
As long as you use you gatsby-autolink-headers plugin can get rid of the slugger implementation. If you need, you can use the normal link value (heading.value), the slug field (if provided), or sanitize it using a custom function like:
function slugify (text) {
return text
.toString()
.toLowerCase()
.normalize(`NFD`)
.trim()
.replace(/\s+/g, `-`)
.replace(/[^\w-]+/g, ``)
.replace(/--+/g, `-`);
};
<Link to={"#" + slugify(heading.value)}>

Images Won't Load with Array in React

I've been trying to learn React over the past few weeks, and have decided to go through the LamaDev tutorial series. Yesterday I started with the portfolio tutorial (https://www.youtube.com/watch?v=hQjlM-8C4Ps&t=2798s) but have been stuck with trying to load images in my array.
I went ahead and built a component called 'Product' which the code can be found below. After that I followed the instructions and built the ProductList component which is suppose to show each of my products that are in my data.js file. I have gone and posted those below.
The problem I am running in to is that if I use a random img link from the internet the image gets imported into my product and show through my product list. However this is not what I am wanting to do since there are some images of my own I wanted to use.
When my Product.jsx tried to use a image I have saved in src/assets/img/ the img won't load. I tried using the require tag but it still is not working. I have also gone ahead and uploaded everything to my github page which can be found here and used as a reference.
I'm really not sure what I've done wrong here since everything looks right, but still know the issue is falling between the keyboard and the chair.
Thanks for any help
Product.jsx
import "./product.css";
const Product = ({ img, link }) => {
return (
<div className="p">
<div className="p-browser">
<div className="p-circle"></div>
<div className="p-circle"></div>
<div className="p-circle"></div>
</div>
<a href={link} target="_blank" rel="noreferrer">
<img src={img} alt="" className="p-img" />
</a>
</div>
);
};
export default Product;
ProductList.jsx
import Product from "../product/Product";
import "./productList.css";
import { products } from "../../data";
const ProductList = () => {
return (
<div className="pl">
<div className="pl-texts">
<h1 className="pl-title">Create & inspire. It's Jon</h1>
<p className="pl-desc">
Jon Snow is a creative portfolio that your work has been waiting for.
Beautiful homes, stunning portfolio styles & a whole lot more awaits
inside.
</p>
</div>
<div className="pl-list">
{products.map((item) => (
// console.log(item)
<Product key={item.id} img={item.img} link={item.link} />
))}
</div>
</div>
);
};
export default ProductList;
data.js
export const products = [
{
id: 1,
img: require("./assets/img/theBestTestSite.jpg"),
link: "http://google.com",
},
{
id: 2,
img: require("./assets/img/theBestTestSite.jpg"),
link: "http://google.com",
},
{
id: 3,
img: require("./assets/img/theBestTestSite.jpg"),
link: "http://google.com",
},
];
In data.js try to import images first instead of require
import img1 from "./assets/img/theBestTestSite.jpg"
export const products = [
{
id: 1,
img: img1,
link: "http://google.com",
},
// and same for others
];

How can i solve this issue ? TypeError: Cannot read properties of undefined (reading 'map') in react.js

Hierarchy Structure Picture : https://prnt.sc/1y2ply6
notes.js(file) :
const notes = [
{
_id: "1",
title: "Day 1 of college",
content:
"I made a few new friends and introduced myself to a lot of new teachers.",
category: "College",
},
{
_id: "2",
title: "Learned some Node JS",
content: "Learned how to create a server in node JS and my first API",
category: "Learning",
},
{
_id: "3",
title: "Watched some Anime",
content: "Finished 2 seasons of Attack on Titan and My Hero academia.",
category: "Entertainment",
},
{
_id: 4,
title: "Started React JS",
content:
"Made my first App in React JS, feels awesome to learn something new. I aim to be a full stack dev someday",
category: "Learning",
},
];
module.exports = notes;
MyNotes.js(file)
import React from 'react'
import { Link } from 'react-router-dom'
import { Accordion, Badge, Button, Card } from 'react-bootstrap'
import MainScreen from '../../components/MainScreen'
import { notes } from '../../data/notes.js'
const MyNotes = () => {
const deleteHandler = (id) => {
if(window.confirm("Are you sure?")){
}
}
return (
<MainScreen title='Welcome Deepak Sarkar'>
<Link to="/createnote">
<Button style={{ marginLeft: 10, marginBottom: 6, size:"lg"}}>
Create New Note
</Button>
</Link>
{
notes.map((note) => (
<Accordion>
<Card style={{ margin: 10 }}>
<Card.Header style={{ display:"flex" }}>
<span style={{
color:"black",
textDecoration:"none",
flex:1,
cursor:"pointer",
alignSelf:"center",
fontSize:18
}}>
<Accordion.Toggle as={Card.Text} variant='link' eventKey="0">
{note.title}
</Accordion.Toggle>
</span>
<div>
<Button href={`/note/${note._id}`}>Edit</Button>
<Button
variant='danger'
className="mx-2"
onClick={() => deleteHandler(note._id)}>Delete</Button>
</div>
</Card.Header>
<Accordion.Collapse eventKey="0">
<Card.Body>
<h4><Badge variant="success">
Category - {note.category}
</Badge></h4>
<blockquote className="blockquote mb-0">
<p>{note.content}</p>
<footer className="blockquote-footer"> Created on -date </footer>
</blockquote>
</Card.Body>
</Accordion.Collapse>
</Card>
</Accordion>
))
}
</MainScreen>
)
}
export default MyNotes
if i remove "{}" from import { notes } from '../../data/notes.js' and add export default notes in notes.js
i get this error :
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of MyNotes.
can anyone help me solve this please ?
It seems you have mixed up which version of react-bootstrap you are using. The Accordion component API changed between v1 (Bootstrap 4.6) and v2 (Bootstrap 5.1).
Here with react-bootstrap v1.6.4 your code works without issue.
V1 Accordion API
This version has the Accordion, Accordion.Toggle, and Accordion.Collapse components.
V2 Accordion API
The v2 version has a few more options with Accordion, Accordion.Item, Accordion.Header, Accordion.Body, Accordion.Button, and Accordion.Collapse.
I suggest sticking to v1.6.4 and keeping your existing code working. If you want, or need, to upgrade then it's a bit unclear what immediate component changes you'll need to have a similarly working UI/UX, though I suspect the new accordion components will replace some of the card components currently used.
You can use either named or default export of the notes though, just don't use the module.exports.
The problem I have find is about the import you do of your notes.js file. Using {} you try to get a "notes" object inside your "notes" array. I have tested it, without {} and leaving your notes.js file as you have it, and work for me.
import notes from '../../data/notes.js'
I hope it can solve your problem.

What is the best way to do a route in next.js

I'm a bit of a beginner at javascript but I am using next routes which uses path-to-regexp under the hood.
I have a page that has a list of links. The route for this page is /locations/s-:specialty-providers with "specialty" being the only dynamic part.
When I click on one of the links on the page, it reroutes me to /locations/s-s-:specialty-providers/:abbr (where both specialty and abbr are dynamic) when it should be rerouting to /locations/s-:specialty-providers/:abbr.
Routes file:
{
name: 'sitemap-providers-location-procedure',
page: 'sitemap/providers/by_location/by_procedure',
pattern: '/locations/:procedure-providers'
},
{
name: 'sitemap-specialties-location-city',
page: 'sitemap/specialties/by_location/by_city',
pattern: '/locations/s-:specialty-providers/:abbr'
},
Next.js will give you routing out of the box with putting your file in pages directory
Let's say you have pages/index.js
import Link from 'next/link'
function getSitemaps () {
return [
{
id:"1",
title: "sitemap-providers-location-procedure",
s: "providers",
abbr: "by_procedure",
},
{
id:"2",
title: "sitemap-specialties-location-city",
s: "specialties",
abbr: "by_city",
}
]
}
const SitemapLink = ({post}) => (
<li>
<Link as={`/sitemap/${post.s}/by_location/${post.abbr}`} href={`/sitemap?title=${post.title}`}>
<a>{post.title}</a>
</Link>
</li>
)
export default () => (
<div>
<h1>Links</h1>
<ul>
{getSitemaps().map((sitemap) => (
<SitemapLink key={sitemap.id} post={sitemap}/>
))}
</ul>
</div>
)
and there is another filepages/sitemap.js
import {withRouter} from 'next/router'
const Page = withRouter((props) => (
<div>
<h1>{props.router.query.title}</h1>
<p>This is the page content.</p>
</div>
))
export default Page
Now you have that dynamic routes.
You don't need any magic or pattern in next.js to create route.

Categories

Resources