This question already has an answer here:
How to access route parameter inside getServerSideProps in Next.js?
(1 answer)
Closed 1 year ago.
I have created simple application, I passed number value menuApi.js to [catId].js, after can't pass catId value is 26(i.e)http://localhost:3000/category/26, Now I pass catId inside of getServersideProps method but not working. What I am missing.
menuApi.js
import React, { Component } from 'react';
import { Grid, Image } from "semantic-ui-react";
import Link from 'next/link';
function MenuApi(props) {
return (
<Grid className="home-icon">
<Grid.Row centered doubling columns={8} mobile>
{props.menu.map((x, i) => (
<Grid.Column centered key={i} Style="width: 9%!important;">
<Link
href={'/category/'+x.id}
>
<Image src={x.image} alt=""/>
</Link>
<Link href={x.category_url}>
<p >{x.store_name}</p>
</Link>
</Grid.Column>
))}
</Grid.Row>
</Grid>
)
}
export default MenuApi;
[catId].js
import { useRouter } from 'next/router'
const Post = (props) => {
console.log(props.ruslt)
return <p>Post: {storeId}</p>
}
const router = useRouter()
const { storeId } = router.query
export async function getServerSideProps(context) {
const offerList = await fetch('http://localhost:3000/api/v4/web/list',{
method:'POST',
body: JSON.stringify(storeId),
headers: { "Content-Type": "application/json" },
})
const offerData = await offerList.json();
const result=offerData.stores;
return {
props: {
result,
},
};
}
export default Post
You are using useRouter hook outside of a functional component. Hooks can only be used inside of a functional component instead of getServerSideProps .
It should be like this:
const Post = (props) => {
const router = useRouter()
const { storeId } = router.query
console.log(props.result)
return <p>Post: {storeId}</p>
}
Also, you should use getStaticProps instead of getServerSideProps. getServerSideProps will render the page on each request, so your response time will increase.
Instead use getStaticProps which will pre-render your page so response time will reduce.
Data Fetching in Next
The following code should fix it. You seem to be passing the props wrong from the getServerSideProps function
import { useRouter } from 'next/router'
const Post = (props) => {
console.log(props.result)
return <p>Post: {props.storeId}</p>
}
const router = useRouter()
const { storeId } = router.query
export async function getServerSideProps(context) {
const offerList = await fetch('http://localhost:3000/api/v4/web/list',{
method:'POST',
body: storeId,
headers: { "Content-Type": "application/json" },
})
const offerData = offerList;
const result=offerData.stores;
return {
props: {
result,
storeId
},
};
}
export default Post
Related
When I click on a link in my /index.js, it brings me to /about.js page.
However, when I'm passing parameter name through URL (like /about?name=leangchhean) from /index.js to /about.js, I don't know how to get it in the /about.js page.
index.js
import Link from 'next/link';
export default () => (
<div>
Click{' '}
<Link href={{ pathname: 'about', query: { name: 'leangchhean' } }}>
<a>here</a>
</Link>{' '}
to read more
</div>
);
Use router-hook.
You can use the useRouter hook in any component in your application.
https://nextjs.org/docs/api-reference/next/router#userouter
pass Param
import Link from "next/link";
<Link href={{ pathname: '/search', query: { keyword: 'this way' } }}><a>path</a></Link>
Or
import Router from 'next/router'
Router.push({
pathname: '/search',
query: { keyword: 'this way' },
})
In Component
import { useRouter } from 'next/router'
export default () => {
const router = useRouter()
console.log(router.query);
...
}
Using Next.js 9 or above you can get query parameters:
With router:
import { useRouter } from 'next/router'
const Index = () => {
const router = useRouter()
const {id} = router.query
return(<div>{id}</div>)
}
With getInitialProps:
const Index = ({id}) => {
return(<div>{id}</div>)
}
Index.getInitialProps = async ({ query }) => {
const {id} = query
return {id}
}
url prop is deprecated as of Next.js version 6:
https://github.com/zeit/next.js/blob/master/errors/url-deprecated.md
To get the query parameters, use getInitialProps:
For stateless components
import Link from 'next/link'
const About = ({query}) => (
<div>Click <Link href={{ pathname: 'about', query: { name: 'leangchhean' }}}><a>here</a></Link> to read more</div>
)
About.getInitialProps = ({query}) => {
return {query}
}
export default About;
For regular components
class About extends React.Component {
static getInitialProps({query}) {
return {query}
}
render() {
console.log(this.props.query) // The query is available in the props object
return <div>Click <Link href={{ pathname: 'about', query: { name: 'leangchhean' }}}><a>here</a></Link> to read more</div>
}
}
The query object will be like: url.com?a=1&b=2&c=3 becomes: {a:1, b:2, c:3}
For those looking for a solution that works with static exports, try the solution listed here: https://github.com/zeit/next.js/issues/4804#issuecomment-460754433
In a nutshell, router.query works only with SSR applications, but router.asPath still works.
So can either configure the query pre-export in next.config.js with exportPathMap (not dynamic):
return {
'/': { page: '/' },
'/about': { page: '/about', query: { title: 'about-us' } }
}
}
Or use router.asPath and parse the query yourself with a library like query-string:
import { withRouter } from "next/router";
import queryString from "query-string";
export const withPageRouter = Component => {
return withRouter(({ router, ...props }) => {
router.query = queryString.parse(router.asPath.split(/\?/)[1]);
return <Component {...props} router={router} />;
});
};
Get it by using the below code in the about.js page:
// pages/about.js
import Link from 'next/link'
export default ({ url: { query: { name } } }) => (
<p>Welcome to About! { name }</p>
)
I know 2 ways to do this:
A Server-Side way, and a Client-Side way.
Method #1: SSR (Server-Side Rendering):
You should use Query Context for that page.
So use getServerSideProps instead of getStaticProps
import React from "react";
export async function getServerSideProps(context) {
const page = (parseInt(context.query.page) || 1).toString();
// Here we got the "page" query parameter from Context
// Default value is "1"
const res = await fetch(`https://....com/api/products/?page=${page}`);
const products = await res.json();
return {props: {products: products.results}}
// will be passed to the page component as props
}
const Page = (props) =>{
const products = props.products;
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>);
}
export default Page
The reason is that: this data cannot be pre-rendered ahead of user's request, so it must be Server-Side Rendered (SSR) on every request.
Static Pages: Use getStaticProps
Changing Content: use getServerSideProps
And here the content is changing based on query Parameters
Reference: https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props
Method #2: Next Router (Client Side):
import {useState, useEffect} from "react";
import { useRouter } from 'next/router'
const Page = () =>{
const [products, setProducts] = useState([]);
const [page, setPage] =useState((useRouter().query.page || 1).toString());
// getting the page query parameter
// Default value is equal to "1"
useEffect(()=>{
(async()=>{
const res = await fetch(`https://....com/api/products/?page=${page}`);
const products = await res.json();
setProducts(products.results);
// This code will be executed only once at begining of the loading of the page
// It will not be executed again unless you cahnge the page
})()
},[page]);
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
export default Page
Reference: https://nextjs.org/docs/api-reference/next/router
If you need to retrieve a URL query from outside a component:
import router from 'next/router'
console.log(router.query)
import { useRouter } from 'next/router';
function componentName() {
const router = useRouter();
console.log('router obj', router);
}
We can find the query object inside a router using which we can get all query string parameters.
Using {useRouter} from "next/router"; helps but sometimes you won't get the values instead u get the param name itself as value.
This issue happens when u are trying to access query params via de-structuring like:
let { categoryId = "", sellerId = "" } = router.query;
and the solution that worked for me is try to access the value directly from query object:
let categoryId = router.query['categoryId'] || '';
let sellerId = router.query['sellerId'] || '';
Post.getInitialProps = async function(context) {
const data = {}
try{
data.queryParam = queryString.parse(context.req.url.split('?')[1]);
}catch(err){
data.queryParam = queryString.parse(window.location.search);
}
return { data };
};
import { useRouter } from 'next/router'
const Home = () => {
const router = useRouter();
const {param} = router.query
return(<div>{param}</div>)
}
Also you can use getInitialProps, more details refer the below tutorial.
get params from url in nextjs
What worked for me in Nextjs 13 pages in the app directory (SSR)
Pass params and searchParams to the page:
export default function SomePage(params, searchParams) {
console.log(params);
console.log(searchParams);
return <div>Hello, Next.js!</div>;
With some builds there may be a bug that can be solved by adding:
export const dynamic='force-dynamic';
especially when deploying on Vercel.
ref: https://beta.nextjs.org/docs/api-reference/file-conventions/page#searchparams-optional
https://github.com/vercel/next.js/issues/43077
I thought that relay modern implemented a system whereby it would not try to fetch data until it was rendering the component that declared it. I am talking about fragment components. I have tried to test this but it is fetching all the data.
import React from "react";
import { Environment, Network, RecordSource, Store } from "relay-runtime";
import {
RelayEnvironmentProvider,
} from "react-relay/hooks";
import "./App.css";
import QueryLoaderComponent from "./QueryLoaderComponent";
import QueryComponent from "./QueryComponent";
async function fetchGraphQL(text: string, variables: Record<any, any>) {
// Fetch data from GitHub's GraphQL API:
const response = await fetch("https://countries.trevorblades.com/", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
query: text,
variables,
}),
});
// Get the response as JSON
return await response.json();
}
async function fetchRelay(params: any, variables: any) {
console.log(
`fetching query ${params.name} with ${JSON.stringify(variables)}`
);
return fetchGraphQL(params.text, variables);
}
// Export a singleton instance of Relay Environment configured with our network function:
const environment = new Environment({
network: Network.create(fetchRelay),
store: new Store(new RecordSource()),
});
function App() {
return (
<RelayEnvironmentProvider environment={environment}>
{/* <QueryLoaderComponent /> */}
<QueryComponent />
</RelayEnvironmentProvider>
);
}
export default App;
import { useState } from "react";
// #ts-ignore
import graphql from "babel-plugin-relay/macro";
import { QueryComponentQuery } from "./__generated__/QueryComponentQuery.graphql";
import { PreloadedQuery, useLazyLoadQuery, usePreloadedQuery } from "react-relay";
// import FragmentComponent from "./FragmentComponent";
const query = graphql`
query QueryComponentQuery($id: ID!) {
country(code: $id) {
name
...FragmentComponent_country
}
}
`;
interface Props {
// queryRef: PreloadedQuery<QueryComponentQuery>;
}
const QueryComponent = ({
// queryRef
}: Props) => {
const data = useLazyLoadQuery<QueryComponentQuery>(query, { id: "US"});
const [showContinent, setShowContinent] = useState(false);
return (
<div>
<button onClick={() => setShowContinent(!showContinent)}>
{showContinent ? "Hide" : "Show"} continent
</button>
<h1>{data.country?.name}</h1>
{/* <ul>
{data.countries.map((country: any) => (
<li key={country.name}>
{country.name}{" "}
{showContinent && <FragmentComponent country={country} />}
</li>
))}
</ul> */}
</div>
);
};
export default QueryComponent;
import { useFragment } from "react-relay";
// #ts-ignore
import graphql from "babel-plugin-relay/macro";
import { FragmentComponent_country$key } from "./__generated__/FragmentComponent_country.graphql";
export const fragment = graphql`
fragment FragmentComponent_country on Country {
continent {
name
}
}
`;
interface Props {
country: FragmentComponent_country$key;
}
const FragmentComponent = ({ country }: Props) => {
const data = useFragment(fragment, country);
return <div>{data.continent.name}</div>;
};
export default FragmentComponent;
this is fetching the data for the fragment component even though it is not rendering the fragment component. is there a way to defer it until it is rendering the component?
use
React Suspense
on the fragment or anywhere where fetching happens as wrapper
Hello im going to get data from API using this https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props
but I'm using Axios instead of default like doc, same as the doc passing data through the props, already implement this but instead return data its return 500 internal server which is it works when on the localhost.
this is my home.js
import axios from "axios";
import Featured from "../components/Featured";
import ProductList from "../components/ProductList";
import styles from "../styles/Home.module.css";
export default function Home({ productList }) {
return (
<div className={styles.container}>
<Featured />
<ProductList productList={productList} />
</div>
);
}
export const getServerSideProps = async () => {
const res = await axios.get(
"http://localhost:3000/api/products" ||
"https://marrs-id.vercel.app/api/products"
);
const data = await res.data;
return {
props: {
productList: data,
},
};
};
am I missing something here?
You might want to store your API path in a .env file at the root of your project. So it can be used everywhere in your app and easy to maintain ?
Also I guess you can use Fetch instead of Axios for this case, something like this :
export const getServerSideProps = async () => {
const productListRes = await fetch(
`${process.env.API_ROOT}/api/products`
);
const productList = await productListRes.json();
return {
props: {
productList,
},
};
};
Here are 3 basic components searchBar which refers to the search bar form and, the searchPage component which displays the search results, and of course, the app component which contains them all.
mechanism:
the user submits an input in the searchBar component, the
handleSubmit function gets fired, which changes the state of
setSearchedProducts to the input value, by useContext AND
getting pushed to the ("/SearchPage") by history.push() .
import {useState, useContext } from "react";
import { useHistory } from "react-router-dom";
import { LocaleContext } from "../../../LocaleContext";
const SearchBar = () => {
const history = useHistory();
const {setSearchedTerm} = useContext(LocaleContext);
const handleSubmit = (SearchTerm) => {
setSearchedProducts(SearchTerm)
history.push("/SearchPage");
}
return (
<form>
<input onSubmit={(e) => handleSubmit(e.target.value)}>
</input>
<button type="submit">Submit</button>
</form>
)
}
export default SearchBar
the value gets sent to the app component by react context and
the state gets set to the value while still pushing to the
("/searchPage").
import { useState, useMemo } from "react";
import { searchBar, searchPage } from "./components";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import {LocaleContext} from "./LocaleContext"
const App = () => {
const [searchedTerm, setSearchedTerm] = useState("");
const providerValue = useMemo(() => ({searchedTerm, setSearchedTerm}),
[searchedTerm, setSearchedTerm])
return (
<Router>
<LocaleContext.Provider value={providerValue}>
<searchBar />
<Switch>
<Route exact path="/SearchPage">
<SearchPage />
</Route>
</Switch>
</LocaleContext.Provider>
</Router>
);
}
export default (App);
displaying the searchPage component, which gets the state value
by using useContext, and with useEffect, the fetchProducts()
function gets fired, that fetches a set of products based on the
state value.
import {useState, useEffect, useContext} from 'react';
import { LocaleContext } from "../../LocaleContext";
const SearchPage = ({}) => {
const [products, setProducts] = useState([]);
const {searchedTerm} = useContext(LocaleContext);
const fetchProducts = (term) => {
setLoading(true);
const url = new URL(
"https://example/products"
);
let params = {
"query": term
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
let headers = {
"Accept": "application/json",
"Content-Type": "application/json",
};
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => {
setProducts(json);
});
}
useEffect(() => {
fetchProducts(searchedProducts)
}, [])
return (
{
products.map(product => (
<div>
{product.name}
</div>
))
}
)
}
export default SearchPage
Issues:
when the router changes to the ("/searchPage") component state value get lost, meaning it returns to "" value. ?
lesser problem, if the user sends an empty string (" "), and the API needs a value or it will give an error, what is the solution to that?
is there a way of keeping the value after reloading the page?
import {createContext} from "react";
export const LocaleContext = createContext(null);
this is the localeContext component if needed.
you have to add e.preventDefault() in your onSubmit handler. Otherwise you're getting a page reload which causes a state loss.
I noticed "setSearchedProducts" & "setSearchedTerm" should be the same in your code below. This might be your issue!
const SearchBar = () => {
...
const {setSearchedTerm} = useContext(LocaleContext);
const handleSubmit = (SearchTerm) => {
setSearchedProducts(SearchTerm)
...
}
I 'm traying to send a param to getInitialProp function to made the fecth to the correct json.
here is my code:
hepler.js --> here I made the fetch per se.
export async function getEvents() {
const res = await fetch("https://url/eventos.json");
let new_data = await res.json();
return { events: new_data.data };
}
export async function getDetails(slug) {
const res = await fetch(`https://myurl/${slug}.json`);
let data_detail_event = await res.json();
return { data_detail_event };
}
_app.js // here I have the getInitialProps and works great
import App from "next/app";
import ContextProvider from "../provider/ContextProvider";
import fetch from "isomorphic-unfetch";
import {getEvents, getDetails} from '../helper/index'
export default class MyApp extends App {
static async getInitialProps() {
const events = await getEvents();
return {
events : events.events
};
}
render() {
const { Component, pageProps } = this.props;
return (
<div>
<ContextProvider events={this.props.events} >
<Component {...pageProps} />
</ContextProvider>
</div>
);
}
}
pages/[id].js
import { useRouter } from "next/router";
import Context from "../../config/Context";
/* Components */
import WordCounter from "../../components/word-counter/WordCounter";
function Post(props) {
const router = useRouter();
const context = React.useContext(Context);
return (
<React.Fragment>
<WordCounter />
</React.Fragment>
);
}
Post.getInitialProps = async ({ query}) => {
const detail = await getDetail(query.id) --> here I send the param and it seems never arrive to helper.js, why?
return {detail}
}
export default Post
Where is the problem? HELP!
THAANKS!
i think getInitialProps run in server and your helper function doesn't load there.
use fetch inside getInitialProps .