Nextjs 13 how to implement dynamic title - next.js

I'm having trouble with implementing dynamic title with Next.js 13.
The Documents said, (https://beta.nextjs.org/docs/routing/pages-and-layouts#modifying-head)
Warning: Currently, the <Head> export does not re-render on client-side transition using next/link, only on initial render and reloads. To work around this for <title>, you can use a client component with useEffect that updates document.title. We plan to fix this in a future release.
And then I tried with useEffect to implement dynamic title,
'use client';
import { useEffect } from "react";;
import Head from 'next/head';
interface Props {
params: {slug : string},
searchParams: {id:string}
};
const getPost = async (id: string) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
const posts = await res.json();
return posts;
};
const Page = ({params, searchParams}: Props) => {
useEffect(() => {
const setDocumentTitle = async () => {
const post = await getPost(params.slug);
document.title = post.title;
}
setDocumentTitle();
}, [params.slug]);
return (<>
<Head>
<title>Blog Post</title>
</Head>
<div>
<h1>Blog Post</h1>
<p>Slug: {params.slug}</p>
<p>Id: {searchParams.id}</p>
</div>
</>);
};
export default Page;
but it is not working. still title is not changed. Is there any way?
You can reproduce it here.codesandbox
The first few times seem to be working well, but the document title does not change if you continue to go back and click Go to Post1 (next/link).

Related

React Query - useQuery callback dependent on route parameter? [duplicate]

When page is refreshed query is lost, disappears from react-query-devtools.
Before Next.js, I was using a react and react-router where I would pull a parameter from the router like this:
const { id } = useParams();
It worked then. With the help of the, Next.js Routing documentation
I have replaced useParams with:
import { usePZDetailData } from "../../hooks/usePZData";
import { useRouter } from "next/router";
const PZDetail = () => {
const router = useRouter();
const { id } = router.query;
const { } = usePZDetailData(id);
return <></>;
};
export default PZDetail;
Does not work on refresh. I found a similar topic, but manually using 'refetch' from react-query in useEffects doesn't seem like a good solution. How to do it then?
Edit
Referring to the comment, I am enclosing the rest of the code, the react-query hook. Together with the one already placed above, it forms a whole.
const fetchPZDetailData = (id) => {
return axiosInstance.get(`documents/pzs/${id}`);
};
export const usePZDetailData = (id) => {
return useQuery(["pzs", id], () => fetchPZDetailData(id), {});
};
Edit 2
I attach PZList page code with <Link> implementation
import Link from "next/link";
import React from "react";
import TableModel from "../../components/TableModel";
import { usePZSData } from "../../hooks/usePZData";
import { createColumnHelper } from "#tanstack/react-table";
type PZProps = {
id: number;
title: string;
entry_into_storage_date: string;
};
const index = () => {
const { data: PZS, isLoading } = usePZSData();
const columnHelper = createColumnHelper<PZProps>();
const columns = [
columnHelper.accessor("title", {
cell: (info) => (
<span>
<Link
href={`/pzs/${info.row.original.id}`}
>{`Dokument ${info.row.original.id}`}</Link>
</span>
),
header: "Tytuł",
}),
columnHelper.accessor("entry_into_storage_date", {
header: "Data wprowadzenia na stan ",
}),
];
return (
<div>
{isLoading ? (
"loading "
) : (
<TableModel data={PZS?.data} columns={columns} />
)}
</div>
);
};
export default index;
What you're experiencing is due to the Next.js' Automatic Static Optimization.
If getServerSideProps or getInitialProps is present in a page, Next.js
will switch to render the page on-demand, per-request (meaning
Server-Side Rendering).
If the above is not the case, Next.js will statically optimize your
page automatically by prerendering the page to static HTML.
During prerendering, the router's query object will be empty since we
do not have query information to provide during this phase. After
hydration, Next.js will trigger an update to your application to
provide the route parameters in the query object.
Since your page doesn't have getServerSideProps or getInitialProps, Next.js statically optimizes it automatically by prerendering it to static HTML. During this process the query string is an empty object, meaning in the first render router.query.id will be undefined. The query string value is only updated after hydration, triggering another render.
In your case, you can work around this by disabling the query if id is undefined. You can do so by passing the enabled option to the useQuery call.
export const usePZDetailData = (id) => {
return useQuery(["pzs", id], () => fetchPZDetailData(id), {
enabled: id
});
};
This will prevent making the request to the API if id is not defined during first render, and will make the request once its value is known after hydration.

Next.js Returning error on build with styled components _document.js

I'm using a _document.js to secure that the nextjs will render the page using styled-components(this solve the break on reload), but i'm getting to errors.
I tried to solve it by upgrading next, react and also canary, but without sucess.
Bellow you can find the code and also the errors displayed.
import Document from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
}
Errors displayed:
./pages/_document.js
1:1 Error: next/document should not be imported outside of pages/_document.js. See https://nextjs.org/docs/messages/no-document-import-in-page. #next/next/no-document-import-in-page
12:27 Error: Component definition is missing display name react/display-name
info - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules
error Command failed with exit code 1.

How to return a 404 Not Found page and HTTP status when an invalid parameter of a dynamic route is passed in Next.js?

For example, I have a dynamic route /blog/[article-id].
When visiting an existing blog post /blog/id-that-exist, it works as expected, and now I want to handle the case /blog/id-that-does-not-exist properly.
The code in /blog/[id].jsx looks something like:
export const getStaticPaths async () => {
return {
fallback: true,
paths: (await sequelize.models.Article.findAll()).map(
article => {
return {
params: {
pid: article.slug,
}
}
}
),
}
}
export const getStaticProps async () => {
// Try to get it from the database. Returns none if does not exist.
const article = await sequelize.models.Article.findOne({
where: { slug: pid },
});
return { props: { article: article } };
}
const ArticlePage = (props) => {
// This can happen due to fallback: true while waiting for
// a page that was not rendered at build time to build.
const router = useRouter()
if (router.isFallback) {
return <div>loading</div>;
}
return (
<div>{props.article.body}</div>
);
};
export const getStaticPaths = getStaticPathsArticle;
export const getStaticProps = getStaticPropsArticle;
export default ArticlePage;
I saw this related question: How to handle not found 404 for dynamic routes in Next.js which is calling API? but I'm not sure if it's the same as I'm asking here, as this does not depend on any external API being used.
notFound: true from Next.js 10
Starting in Next.js 10, we can do:
export const getStaticProps async () => {
// Try to get it from the database. Returns none if does not exist.
const article = await sequelize.models.Article.findOne({
where: { slug: pid },
});
if (!article) {
return {
notFound: true
}
}
return { props: { article: article } };
}
as documented at: https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation
When notFound is returned, the rendering function ArticlePage just never gets called, and the default 404 page is returned instead.
Note however that ArticlePage did get
For some reason in development mode:
I don't get the expected 404 HTTP status code
ArticlePage, so if you forgot to handle the fallback case, the it might crash due to missing properties
which was confusing me a bit. But in production mode, everything works as expected.
Workaround before Next.js 10
As shown https://github.com/vercel/next.js/discussions/10960#discussioncomment-1201 you could previously do something like:
const ArticlePage = (props) => {
if (!props.article) {
return <>
<Head>
<meta name="robots" content="noindex">
</Head>
<DefaultErrorPage statusCode={404} />
</>
}
return (
<div>{props.article.body}</div>
);
};
but this is not ideal because it does not set the HTTP return code correctly I believe, and I don't know how to do it.
Tested on Next.js 10.2.2.
I've read your answer regarding the solution after Next.js v.10, but I didn't get what was the problem in showing the expected http 404 code during development.
I use Next.JS v.12 and I get the expected 404 normally in development
import { GetStaticPaths, GetStaticProps } from 'next'
import { useRouter } from 'next/router'
import { ParsedUrlQuery } from 'querystring'
import Loading from '../../components/loading'
export const getStaticPaths: GetStaticPaths = async () => {
//your paths
return { paths, fallback: true }
}
export const getStaticProps: GetStaticProps = async ({ params }: { params?: ParsedUrlQuery }) => {
//get your props
if (!target){
return {notFound: true}
}
return { props: { ... }, revalidate: 86400}
}
function Index({ ... }) {
const router = useRouter()
if (router.isFallback) {
return <Loading />
}
return (
<div>
//my content
</div>
)
}
export default Index
When the target isn't found, it renders my custom 404 component in pages/404.tsx if I created one or just the default 404 page.
This should work normally during development and production.

Not getting API response data in props using getStaticProps in next js

I am new to nextjs. I want to prerender a page on server and want to delay rendering till API call is resolved. to achieve this i am using getStaticProps as mentioned in nextjs official docs.
here is the position of the file in my code structure:-
i am exporting my getStaticProps from index.js
here is the code snippet :-
export const getStaticProps = async () => {
const res = await axios.get(`http://blogexample.com/blog/posts`);
const blogList = await res.data
return {
props: {
blogList
}
}
}
const Blog = (props) => {
const { blogList } = props;
useEffect(() => {
console.log('list',blogList)
},[blogList])
return(
....
);
}
export default Blog;
problem is that, in browser console....my console.log('list',blogList) statement prints undefined
what i am doing wrong
getStaticProps is executed during build time. Use getServerSideProps
instead.

Use async react-select with redux-saga

I try to implement a async react-select (Select.Async). The problem is, we want to do the fetch in redux-saga. So if a user types something, the fetch-action should be triggered. Saga then fetches the record and saved them to the store. This works so far.
Unfortunately loadOptions has to return a promise or the callback should be called. Since the newly retrieved options get propagated with a changing property, I see no way to use Select.Async together with saga to do the async fetch call. Any suggestions?
<Select.Async
multi={false}
value={this.props.value}
onChange={this.onChange}
loadOptions={(searchTerm) => this.props.options.load(searchTerm)}
/>
I had a hack where i assigned the callback to a class variable and resolve it on componentWillReceiveProps. That way ugly and did not work properly so i look for a better solution.
Thanks
redux-saga is for handling side effects like asynchronously receiving options for react-select. That's why you should leave the async stuff to redux-saga. I have never used react-select but by just looking at the documentation I would solve it this way:
Your component gets very simple. Just get value and options from your redux store. optionsRequested is an action creator for the OPTIONS_REQUESTED action:
const ConnectedSelect = ({ value, options, optionsRequested }) => (
<Select
value={value}
options={options}
onInputChange={optionsRequested}
/>
)
export default connect(store => ({
value: selectors.getValue(store),
options: selectors.getOptions(store),
}), {
optionsRequested: actions.optionsRequested,
})(ConnectedSelect)
A saga definition watches for OPTIONS_REQUESTED action that is trigged by onInputChange, loads the data with given searchTerm from server and dispatches OPTIONS_RECEIVED action to update redux store.
function* watchLoadOptions(searchTerm) {
const options = yield call(api.getOptions, searchTerm)
yield put(optionsReceived(options))
}
In other words: Make your Component as pure as possible and handle all side-effect/async calls in redux-saga
I hope this answer was useful for you.
The main idea is that you are capable to dispatch redux actions using application context from
import React from 'react';
import { connect } from 'react-redux';
import Select from '#components/Control/Form/Skin/Default/Select';
import { reduxGetter, reduxSetter, required as req } from '#helpers/form';
import { companyGetTrucksInit } from "#reduxActions/company";
import AppContext from '#app/AppContext';
const FIELD_NAME = 'truck';
export const getReduxValue = reduxGetter(FIELD_NAME);
export const setReduxValue = reduxSetter(FIELD_NAME);
const SelectCompanyTruck = (props) => {
const {
required,
validate=[]
} = props;
const vRules = [...validate];
if (required)
vRules.push(req);
return (
<AppContext.Consumer>
{({ dispatchAction }) => (
<Select
loadOptions={(inputValue, callback) => {
function handleResponse(response) {
const { data: { items } } = response;
const options = items.map(i => ({ label: i.name, value: i.id }));
callback(options);
}
dispatchAction(companyGetTrucksInit, { resolve: handleResponse, inputValue });
}}
name={FIELD_NAME}
{...props}
/>
)}
</AppContext.Consumer>
);
}
export default SelectCompanyTruck;

Resources