最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How can I make my button in NextJS go to another page I created in the same app? - Stack Overflow

programmeradmin0浏览0评论

for contenxt, I'm building a website for a pet rescue charity, I'm using Next.js. I'm new to Nextjs and can't figure out how to get my button or a link to actually work propperly I am currently getting a 404 error This page could not be found, when I'm trying to get the button to take me to another page I've built in my next app.

I've tried a few things.

Can someone help ??

here is the card ponent:

import React from 'react';
import Image from 'next/image';
import Button from '../ponents/Button';
import Link from 'next/link';

export default function PetCard({ pet, onClick }) {

  return (
    <div className="flex flex-col border p-4 rounded-lg hover:shadow-lg transition-shadow bg-white" >
      <Link href="/[petId]" as={`/adote/${pet.name}`} passHref>
        <Button
          label="Ver perfil"
          onClick={onClick}
          className="bg-indigo-500 hover:bg-gray-600 focus-visible:outline-indigo-600 w-32 flex flex-row justify-center items-center"
        /></Link>

    </div>
  )
}

The button is supposed to go to this petId page:

'use server'
import React from 'react';
import { useRouter } from 'next/router';


export default function PetDetailPage() {
  const router = useRouter();
  const pet = JSON.parse(router.query);
 
  return (
    <div className="p-4">
     <Image src={pet.avatar} alt={pet.name} width={600} height={600} className="rounded-md" />
      <h1 className="text-2xl font-bold mt-4">{pet.name}</h1>
      <p className="text-gray-600">{pet.breed}</p>
      <p className="text-gray-600">{pet.id}</p>
      <p className="text-gray-600">{pet.age}</p>
      <p className="text-gray-600">{pet.gender}</p>
      <p className="mt-2">{pet.description}</p>
    </div>
  );
}

I don't mind if its just a placeholder page for now. I have this mock data from a mock api I've made and it fetches with no issues on the main adoption page:

'use client';

import React from 'react';
import CustomizableHero from "../../ponents/HeroScondary";
import PetCard from "../../ponents/PetCard";
import Button from '../../ponents/Button';
import { useState, useEffect } from 'react'

export default function Adote() {
  // mock API
  const [pets, setPets] = useState([])
  const [loading, setLoading] = useState(true)
  const [page, setPage] = useState(1)

  useEffect(() => {
    const fetchPets = async () => {
      setLoading(true)
      const url = new URL('')

      url.searchParams.append('pleted', false)
      url.searchParams.append('page', page)
      url.searchParams.append('limit', 18)
      
      const res = await fetch(url)      
      const data = await res.json()
      setPets(prevPets => [...prevPets, ...data])
      setLoading(false)

    }
    fetchPets()
  },
    [])

  if (loading) {
    return <p>Loading...</p>
  }

  const handlePetClick = (petId) => {
    router.push(`/adote/${petId}`)
  }

  return (
    <div className="relative">
      <CustomizableHero
        backgroundImage="/images/dois-cachorros.png"
        title="Adote"
        subtitle="Encontre o seu melhor amigo"
        color="white"
      />
      {/* search filter */}
      <div className="flex flex-row">
      </div>
      {/* pet cards */}
      <div className="absolute w-[100vw] max-w-screen flex flex-col justify-center ">
        <div className="flex flex-col text-align-center items-center justify-between m-10">
          <h2 className="text-5xl font-bold tracking-tight sm:text-5xl text-gray-500 p-5">Animais buscando uma familia</h2>
          <p className="mt-6 mb-3 text-md leading-8 text-gray-500 text-center p-5">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec euismod, nulla ut condimentum maximus, felis enim rhoncus justo, vel venenatis magna tortor nec tortor. Donec vulputate fermentum fermentum. Curabitur odio dui, consectetur vitae ornare eu, ultricies vel risus</p>
        </div>
        {/* Called displayPetCards() inside JSX code to render the PetCard ponents. */}
        <div className="grid grid-cols-3 gap-4 m-10 p-5">
          {pets.map(pet => (
            <PetCard
              key={pet.id}
              pet={pet}
              onClick={()=> handlePetClick(pet.id)}
            />
          ))}
        </div>
        <p className="flex flex-col text-center mt-5">Visualizados 18 de 661 animais de estimação</p>
        <div className="flex justify-center m-10">
          <Button
            label="Mostrar mais"
            onClick={() => setPage(page + 1)
            }  // Attach the click handler here
            className="bg-indigo-500 hover:bg-gray-600 focus-visible:outline-indigo-600"
          />
        </div>
      </div>
    </div>
  );
};

I've tried useRouter, I've tried getServerSideProps() but nothing works

for contenxt, I'm building a website for a pet rescue charity, I'm using Next.js. I'm new to Nextjs and can't figure out how to get my button or a link to actually work propperly I am currently getting a 404 error This page could not be found, when I'm trying to get the button to take me to another page I've built in my next app.

I've tried a few things.

Can someone help ??

here is the card ponent:

import React from 'react';
import Image from 'next/image';
import Button from '../ponents/Button';
import Link from 'next/link';

export default function PetCard({ pet, onClick }) {

  return (
    <div className="flex flex-col border p-4 rounded-lg hover:shadow-lg transition-shadow bg-white" >
      <Link href="/[petId]" as={`/adote/${pet.name}`} passHref>
        <Button
          label="Ver perfil"
          onClick={onClick}
          className="bg-indigo-500 hover:bg-gray-600 focus-visible:outline-indigo-600 w-32 flex flex-row justify-center items-center"
        /></Link>

    </div>
  )
}

The button is supposed to go to this petId page:

'use server'
import React from 'react';
import { useRouter } from 'next/router';


export default function PetDetailPage() {
  const router = useRouter();
  const pet = JSON.parse(router.query);
 
  return (
    <div className="p-4">
     <Image src={pet.avatar} alt={pet.name} width={600} height={600} className="rounded-md" />
      <h1 className="text-2xl font-bold mt-4">{pet.name}</h1>
      <p className="text-gray-600">{pet.breed}</p>
      <p className="text-gray-600">{pet.id}</p>
      <p className="text-gray-600">{pet.age}</p>
      <p className="text-gray-600">{pet.gender}</p>
      <p className="mt-2">{pet.description}</p>
    </div>
  );
}

I don't mind if its just a placeholder page for now. I have this mock data from a mock api I've made and it fetches with no issues on the main adoption page:

'use client';

import React from 'react';
import CustomizableHero from "../../ponents/HeroScondary";
import PetCard from "../../ponents/PetCard";
import Button from '../../ponents/Button';
import { useState, useEffect } from 'react'

export default function Adote() {
  // mock API
  const [pets, setPets] = useState([])
  const [loading, setLoading] = useState(true)
  const [page, setPage] = useState(1)

  useEffect(() => {
    const fetchPets = async () => {
      setLoading(true)
      const url = new URL('https://mockID.mockapi.io/pets')

      url.searchParams.append('pleted', false)
      url.searchParams.append('page', page)
      url.searchParams.append('limit', 18)
      
      const res = await fetch(url)      
      const data = await res.json()
      setPets(prevPets => [...prevPets, ...data])
      setLoading(false)

    }
    fetchPets()
  },
    [])

  if (loading) {
    return <p>Loading...</p>
  }

  const handlePetClick = (petId) => {
    router.push(`/adote/${petId}`)
  }

  return (
    <div className="relative">
      <CustomizableHero
        backgroundImage="/images/dois-cachorros.png"
        title="Adote"
        subtitle="Encontre o seu melhor amigo"
        color="white"
      />
      {/* search filter */}
      <div className="flex flex-row">
      </div>
      {/* pet cards */}
      <div className="absolute w-[100vw] max-w-screen flex flex-col justify-center ">
        <div className="flex flex-col text-align-center items-center justify-between m-10">
          <h2 className="text-5xl font-bold tracking-tight sm:text-5xl text-gray-500 p-5">Animais buscando uma familia</h2>
          <p className="mt-6 mb-3 text-md leading-8 text-gray-500 text-center p-5">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec euismod, nulla ut condimentum maximus, felis enim rhoncus justo, vel venenatis magna tortor nec tortor. Donec vulputate fermentum fermentum. Curabitur odio dui, consectetur vitae ornare eu, ultricies vel risus</p>
        </div>
        {/* Called displayPetCards() inside JSX code to render the PetCard ponents. */}
        <div className="grid grid-cols-3 gap-4 m-10 p-5">
          {pets.map(pet => (
            <PetCard
              key={pet.id}
              pet={pet}
              onClick={()=> handlePetClick(pet.id)}
            />
          ))}
        </div>
        <p className="flex flex-col text-center mt-5">Visualizados 18 de 661 animais de estimação</p>
        <div className="flex justify-center m-10">
          <Button
            label="Mostrar mais"
            onClick={() => setPage(page + 1)
            }  // Attach the click handler here
            className="bg-indigo-500 hover:bg-gray-600 focus-visible:outline-indigo-600"
          />
        </div>
      </div>
    </div>
  );
};

I've tried useRouter, I've tried getServerSideProps() but nothing works

Share Improve this question edited Oct 31, 2023 at 9:45 alpha_N1618 5605 silver badges20 bronze badges asked Oct 29, 2023 at 18:47 TamaraTamara 231 gold badge1 silver badge5 bronze badges 1
  • Any reason you're trying to use a button? Generally for accessibility you want a link to be for navigation and a button to be for events. You might style a link to look like a button. – Matt Commented Oct 29, 2023 at 19:18
Add a ment  | 

2 Answers 2

Reset to default 5

Don't overplicate things you are already passing pet.id in petCard ponent just link it from there

import React from 'react';
import Image from 'next/image';
import Button from '../ponents/Button';
import Link from 'next/link';

export default function PetCard({ pet, onClick }) {

 return (
  <div className="flex flex-col border p-4 rounded-lg hover:shadow-lg 
    transition-shadow bg-white" >
  <Link href={`/adote/${pet.id}`}>
    <Button
      label="Ver perfil"
      className="bg-indigo-500 hover:bg-gray-600 focus-visible:outline- 
       indigo-600 w-32 flex flex-row justify-center items-center"
    />
  </Link>
  </div>
  )
 }

And then search pet for specific id in PetDetailPage by params

'use server'
import React from 'react';

export default function PetDetailPage({ params }) {
const pet = getPetDetails(params.id)

return (
 <div className="p-4">
  <Image src={pet.avatar} alt={pet.name} width={600} height={600} 
    className="rounded-md" />
  <h1 className="text-2xl font-bold mt-4">{pet.name}</h1>
  <p className="text-gray-600">{pet.breed}</p>
  <p className="text-gray-600">{pet.id}</p>
  <p className="text-gray-600">{pet.age}</p>
  <p className="text-gray-600">{pet.gender}</p>
  <p className="mt-2">{pet.description}</p>
 </div>
 );
}

Heres a example code I made : Explaination:

  • there is a pets page which shows all pets name,
  • It contains 2 elements 1st is Link tag & 2nd is a Button as link
  • Take a look at folder structure, focus on pets folder
  • On pets page I have shown pets data (you may show it using ur card ponent) using URL present in your code.

Folder Structure :

projectName
├── .gitignore
├── jsconfig.json
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│   ├── images
│   ├── next.svg
│   └── vercel.svg
├── README.md
├── src
│   └── app
│       ├── api
│       ├── p
│       │   ├── LinkButton.js
│       ├── favicon.ico
│       ├── globals.css
│       ├── layout.js
│       ├── page.js
│       ├── pets
│       │   ├── page.js
│       │   └── [id]
│       │       └── page.js
└── tailwind.config.js
  • now I will show pets data, & from it i will pass ID to Link tag & Button ponent.

  • Button Component is client side bcoz of event click.

  • Now if someone clicks on link or button, they will get navigated to pets/id

  • Using url present in code I appended id to it get details of pet by id. Details page is under folder pets/[id]/page.js

You may read this for more clarity of concepts :

  • Routing Fundamentals : https://nextjs/docs/app/building-your-application/routing

  • Dynamic Routes :
    https://nextjs/docs/app/building-your-application/routing/dynamic-routes

  • Linking and Navigating :
    https://nextjs/docs/app/building-your-application/routing/linking-and-navigating

  • Server Components :
    https://nextjs/docs/app/building-your-application/rendering/server-ponents

  • Client Components :
    https://nextjs/docs/app/building-your-application/rendering/client-ponents

  • When to use Server and Client Components? :
    https://nextjs/docs/app/building-your-application/rendering/position-patterns#when-to-use-server-and-client-ponents

All Pets Detail Page: projectName\src\app\pets\page.js

import Link from "next/link"
import LinkButton from "../p/LinkButton"

async function GetPets() {
    let data = await fetch('https://653bf08fd5d6790f5ec7a989.mockapi.io/pets')
    let PetsData = await data.json()
    // console.log(PetsData);
    // LOGGED IN TERMINAL BCOZ ITS SERVER-SIDE

    return PetsData
}

export default async function Pets() {


    const PetsData = await GetPets()

    return (
        <div>
            <h1>All Pets !</h1>
            {
                PetsData.map((p, i) => {
                    return (
                        <div style={{
                            border: "1px solid black",
                            padding: "25px",
                            margin: '25px'
                        }}
                            key={i}
                        >
                            <img src={p.avatar} width="250" alt="" />
                            <h3>{p.name}</h3>
                            <Link style={{ color: "blue" }} href={`/pets/` + p.id}> VIew </Link>

                            <LinkButton ID={p.id} />
                        </div>
                    )
                })
            }

        </div>
    )
}

Pet Detail page : projectName\src\app\pets\[id]\page.js

async function GetDetailsByID(ID) {
    let data = await fetch('https://653bf08fd5d6790f5ec7a989.mockapi.io/pets/' + ID)

    // I GOT THIS URL BY TRYING TO APPEND ID TO URL PROVIDED IN CODE

    let PetDetails = await data.json()
    // console.log(Object.keys(PetDetails));
    // LOGGED IN TERMINAL BCOZ ITS SERVER-SIDE


    return PetDetails
}

export default async function DetailsPage({ params }) {
    let Details = await GetDetailsByID(params.id)
    // AFTER GETTING ID I WILL PASS IT THE API CALL

    // THIS PAGE WILL BE SERVER RENDERED

    return (
        <div>
            <h1>Full Pet Deyails :</h1>
            <img src={Details.avatar} alt="" />
            <p className="text-gray-600">{Details.name}</p>
            <p className="text-gray-600">{Details.id}</p>
            <p className="text-gray-600">{Details.age}</p>
            <p className="text-gray-600">{Details.Gender}</p>
            <p className="text-gray-600">{Details.breed}</p>
            <p className="text-gray-600">{Details.description}</p>
            <p className="text-gray-600">{Details.createdAt}</p>




        </div>
    )
}

LinkButton.js Component projectName\src\app\p\LinkButton.js under p folder

'use client'
import { useRouter } from 'next/navigation'
import React from 'react'

const LinkButton = ({ ID }) => {
    const MyRouter = useRouter()

    // IF YOU WANT TO USE A BUTTON THEN IT COMES UNDER CLIENT SIDE 
    // RENDERING BECAUSE OF CLICK EVENT


    // HERE IM PASSING THE ID TO LINK & USING ROUTER TO NAVIGATE
    return (
        <button onClick={() => {
            MyRouter.push(`/pets/` + ID)
        }}>
            Button
        </button>
    )
}

export default LinkButton

Instead of using button you can style that Link tag to look like a button !

Output:

  1. Goto http://localhost:3000/pets
  2. You will a card like layout, with Link Tag named as View in blue color & Button named as Button.
  3. Click on them you will go on a page ex. 1st pet was clicked http://localhost:3000/pets/1
  4. On this page details are fetched by ID.
  5. Open console & then open Network Tab click on pets, then on right hand side click on preview you will see page is pre-rendered !
  6. Siilarly for details page, you will id in Network Tab click on preview you will see page is pre-rendered.

Comment to ask for any doubts.

This code will be deleted from myside(personal puter).

发布评论

评论列表(0)

  1. 暂无评论