Router.push not happening until data on next page is fetched
Unanswered
Barbary Lion posted this in #help-forum
Barbary LionOP
Hey all, I hope you're well! I'm relatively new to doing things like this with Next so I hope my question isn't very basic!
Essentially, I have a search bar (Client component), and a results page (Server component). When the user clicks "search" within the search bar, it pushes a query string to the url from the search bar contents. Then, the results component takes that query string and uses it to get a response from the external API.
Ideally, I want the URL to instantly reflect the changes upon clicking of the search button, and then the results take their time to fetch and load. Instead, the URL only seems to update once the data is fetched. (Video attached)
I've attached an image of my file structure as well and some relevant code snippits below:
classSearch/classList/page.js:
getData function:
classSearch/layout.js:
classSearch/page.js:
Essentially, I have a search bar (Client component), and a results page (Server component). When the user clicks "search" within the search bar, it pushes a query string to the url from the search bar contents. Then, the results component takes that query string and uses it to get a response from the external API.
Ideally, I want the URL to instantly reflect the changes upon clicking of the search button, and then the results take their time to fetch and load. Instead, the URL only seems to update once the data is fetched. (Video attached)
I've attached an image of my file structure as well and some relevant code snippits below:
classSearch/classList/page.js:
import { getData } from "@/app/UtilFunctions";
export default async function Page({ searchParams }) {
const data = await getData(searchParams)
console.log(searchParams)
return (
<div>
{data?.classes.map((classInfo, index) => (
<div key={index}>
<h3>{classInfo.CLAS.TITLE || 'No Title Available'}</h3>
</div>
))}
</div>
)
}getData function:
export async function getData(searchParams) {
const res = await fetch('https://eadvs-cscc-catalog-api.apps.asu.edu/catalog-microservices/api/v1/search/classes?&refine=Y&' + queryString.stringify(searchParams), {
headers: {
"Authorization": "Bearer null"
}
})
if (!res.ok) {
throw new Error('Failed to fetch data')
}
return res.json()
}classSearch/layout.js:
import NewClassSearch from "@/app/components/newClassSearch";
export default function Layout({ children }) {
return (
<main>
<div>
<NewClassSearch />
</div>
<div>{children}</div>
</main>
)
}classSearch/page.js:
import Link from "next/link"
import NewClassSearch from "../components/newClassSearch"
export default function Page({ searchParams }) {
return (
<div>
test
</div>
)
}7 Replies
Barbary LionOP
and lastly:
components/newClassSearch.js:
components/newClassSearch.js:
"use client"
import Image from "next/image";
import magnifyingGlass from "@/public/magnifying-glass-svgrepo-com.svg";
import { useState } from "react";
import isEmpty from "lodash.isempty";
import queryString from 'query-string';
import { useRouter } from "next/navigation";
import Link from "next/link";
const getQueryStringFromState = (data) => {
const params = {
term: isEmpty(data.term) ? "2191" : data.term,
subject: isEmpty(data.subject) ? undefined : data.subject,
catalogNbr: isEmpty(data.catalogNbr) ? undefined : data.catalogNbr,
keywords: isEmpty(data.keywords) ? undefined : data.keywords
}
return queryString.stringify(params)
}
export default function NewClassSearch() {
const router = useRouter()
const [classState, setClassState] = useState({
term: "",
subject: "",
catalogNbr: "",
keywords: ""
})
const handleChange = (event) => {
const name = event.target.name;
const val = event.target.value;
setClassState(prevState => ({
...prevState,
[name]: val
}));
};
console.log('Updated State:', classState)
return (
<div className="md:w-1/2 border-gray-300 rounded-xl border-solid border flex flex-col items-center p-2 focus:border-red-500">
<Link href="/classSearch"><div className="text-2xl">Course Search</div></Link>
<div className="flex h-1/3 w-full justify-center m-6">
<div className="w-2/3 rounded-xl border-solid border transition-all hover:border-red-500 flex justify-between focus-within:border-red-500">
<input
type="text"
name="subject"
onChange={handleChange}
className="rounded-xl p-2 w-11/12 outline-0 border-none"
placeholder="Search for classes. Either by name, number, or keywords"
/>
<button className="flex justify-center items-center">
<Image src={magnifyingGlass} width={25} height={25} className="m-2" />
</button>
</div>
</div>
<div className="text-xl p-2 w-1/2 flex justify-around">
<button onClick={() => router.push("/classSearch/classList?" + getQueryStringFromState(classState))} className="w-32 border-gray-300 rounded-md border-solid border bg-red-500 text-white">
Search
</button>
<button className="text-xl p-2 w-32 border-gray-300 rounded-md border-solid border bg-red-500 text-white">
Filters
</button>
</div>
</div>
);
}hi, you could use with suspense to display loading state
import { getData } from "@/app/UtilFunctions";
import { Suspense } from "react";
export default function Page({ searchParams }) {
return (
<div>
<Suspense
key={JSON.stringify(searchParams)}
fallback={<div>loading...</div>}
>
<SearchResult searchParams={searchParams} />
</Suspense>
</div>
);
}
async function SearchResult() {
const data = await getData(searchParams);
console.log(searchParams);
return data && data?.length > 1 ? (
data?.classes.map((classInfo, index) => (
<div key={index}>
<h3>{classInfo.CLAS.TITLE || "No Title Available"}</h3>
</div>
))
) : (
<div>no results</div>
);
}Barbary LionOP
I messed with suspense a bit and it didn't seem to work. Let me use it in the way you mentioned and I'll let you know!
Barbary LionOP
still doesn't work, sadly
what is not working?
Barbary LionOP
The same issue I initially outlined
can you show the code you have now?