NextJS build and deployment error with Vercel - javascript

I keep having errors trying to deploy my Nextjs app to vercel:
Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error
TypeError: (0 , react_development_.useState) is not a function or its return value is not iterable
at Categories (/vercel/path0/.next/server/chunks/930.js:122:72)
at d (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:33:498)
at bb (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:36:16)
at a.b.render (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:42:43)
at a.b.read (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:41:83)
at Object.exports.renderToString (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:52:138)
at Object.renderPage (/vercel/path0/node_modules/next/dist/server/render.js:686:46)
at Object.defaultGetInitialProps (/vercel/path0/node_modules/next/dist/server/render.js:316:51)
at Function.getInitialProps (/vercel/path0/.next/server/pages/_document.js:514:20)
at Object.loadGetInitialProps (/vercel/path0/node_modules/next/dist/shared/lib/utils.js:69:29)
I tried npm run build locally and I get this error:
> Build error occurred
Error: Export encountered errors on following paths:
/
/post/[slug]: /post/first-post
/post/[slug]: /post/how-html-css-js-work
/post/[slug]: /post/nextjs
/post/[slug]: /post/react-hooks
So I assume it has to be something inside my index page and /post/[slug] page. I tried everything like setting getStaticPaths fallback to false and using optional chaining everywhere but I still get the error.
Can someome please help me out, it is so depressing when I finished the project and I can run it in my localhost but and failed in the deployment/build time.
My / page:
import Head from "next/head";
import { PostCard, Categories, PostWidget } from "../components";
import { getPosts } from "../services";
export default function Home({ posts }) {
const sortedPosts = posts.sort(
(a, b) => new Date(b.node.createdAt) - new Date(a.node.createdAt)
);
return (
<>
<Head>
<title>JBLOG | Home</title>
</Head>
<section className="bg-zinc-200 dark:bg-gray-500 transition-colors px-5">
<div className="max-w-7xl mx-auto py-10">
<div className="grid grid-cols-1 md:grid-cols-12 gap-12">
<div className="md:col-span-8 col-span-1 ">
{sortedPosts?.map((post) => (
<PostCard key={post.node.title} post={post.node} />
))}
</div>
<div className="md:col-span-4 col-span-1">
<div className="md:sticky relative md:top-[110px]">
<PostWidget />
<Categories />
</div>
</div>
</div>
</div>
</section>
</>
);
}
export async function getStaticProps() {
const posts = (await getPosts()) || [];
return {
props: {
posts,
},
};
}
My /post/slug page:
import React from "react";
import Head from "next/head";
import { getPosts, getPostDetails } from "../../services";
import {
PostDetail,
Categories,
PostWidget,
Author,
Comments,
CommentsForm,
} from "../../components";
import { useRouter } from "next/router";
const Post = ({ post }) => {
const router = useRouter();
if (router.isFallback) {
return <div>Loading...</div>;
}
return (
<>
<Head>
<title>{post?.title}</title>
</Head>
<section className="bg-zinc-200 dark:bg-gray-500 transition-colors px-5">
<div className="max-w-7xl mx-auto py-10">
<div className="grid grid-cols-1 md:grid-cols-12 md:gap-6">
<div className="md:col-span-8 col-span-1 ">
<PostDetail post={post} />
<CommentsForm slug={post?.slug} />
<Comments slug={post?.slug} />
</div>
<div className="md:col-span-4 col-span-1">
<div className="md:sticky relative md:top-[110px]">
<Author author={post?.author} />
<PostWidget
slug={post?.slug}
categories={post?.categories?.map(
(category) => category.slug
)}
/>
<Categories />
</div>
</div>
</div>
</div>
</section>
</>
);
};
export default Post;
export const getStaticProps = async (context) => {
const data = await getPostDetails(context.params.slug);
return {
props: {
post: data,
},
};
};
export const getStaticPaths = async () => {
const posts = await getPosts();
return {
paths: posts.map((post) => ({ params: { slug: post.node.slug } })),
fallback: true,
};
};

what's your npm run build?
https://nextjs.org/docs/basic-features/data-fetching
fallback: true is not supported when using next export.

Related

How do I fix the Remix Error: useFetcher must be used within a data router?

I'm new to Remix (and backend programming in general) and feeling lost troubleshooting this. I'm trying to UseFetcher to allow for non-navigational data mutations in a "todo-like" application. Remix docs doesn't explicitly say I need to be using it within a data router, and the examples don't clear up my confusion at all.
Here's what my App component looks like in root.tsx:
export default function App() {
return (
<html lang="en" className="h-full">
<head>
<Meta />
<Links />
</head>
<body className="h-full">
<Outlet />
<ScrollRestoration />
<Scripts />
<LiveReload />
</body>
</html>
);
}
And my routes/goods.tsx for displaying a list of items (much of this is adapted from the default Indie Stack):
export async function action({ request }: ActionArgs) {
const formData = await request.formData();
const title = formData.get("title");
const id = formData.get("id");
if (typeof title !== "string" || title.length === 0) {
return json(
{ errors: { title: "Title is required" } },
{ status: 400 }
);
}
const good = await updateGood({ title, id });
return null;
}
export default function GoodsPage() {
const data = useLoaderData<typeof loader>();
const user = useUser();
return (
<div className="flex h-full min-h-screen flex-col">
<Outlet />
<main className="flex h-full bg-white">
<div className="h-full w-80 border-r bg-gray-50">
{data.completedGoodListItems.length === 0 ? (
<p className="p-4">No goods yet</p>
) : (
<>
<h2>Incomplete</h2>
<ol>
{data.completedGoodListItems.map((good) => (
<GoodItem key={good.id} good={good}></GoodItem>
))}
</ol>
</>
)}
<>
<h2>Completed</h2>
<ol>
{data.incompleteGoodListItems.map((good) => (
<GoodItem key={good.id} good={good}></GoodItem>
))}
</ol>
</>
</div>
<Form action="/logout" method="post">
<button
type="submit"
className="rounded bg-slate-600 py-2 px-4 text-blue-100 hover:bg-blue-500 active:bg-blue-600"
>
Logout
</button>
</Form>
</main>
</div>
);
}
function GoodItem ({ good }) {
const fetcher = useFetcher();
return (
<li>
<fetcher.Form method="post">
<input type="text" defaultValue={good.title}></input>
</fetcher.Form>
</li>
)}
This results in Error: useFetcher must be used within a data router.
So then I try to follow the instructions for encapsulating the App within a data router using createBrowserRouter which leads me to writing this code in my root.tsx:
async function loader({ request }: LoaderArgs) {
return json({
user: await getUser(request),
});
}
const router = createBrowserRouter([
{
path: "/",
element: <App />,
// loader: rootLoader,
children: [
{
path: "/goods",
element: <GoodsPage />,
// loader: loaderName,
},
],
},
]);
// #ts-ignore
ReactDOM.createRoot(document.getElementById("root")).render(
<RouterProvider router={router} />
);
export default function App() {
return (
<html lang="en" className="h-full">
<head>
<Meta />
<Links />
</head>
<body className="h-full">
<Outlet />
<ScrollRestoration />
<Scripts />
<LiveReload />
</body>
</html>
);
}
I didn't know what to add for the loaders for each element. I tried assigning the async loader to a const and adding it into the constructor for router, but I received the error: The expected type comes from property 'loader' which is declared here on type 'RouteObject' so I just left it blank.
This code results in ReferenceError: document is not defined certainly because I don't have the syntax or structure correct for this router. Can someone provide some guidance on how I should be using createBrowserRouter in this context? I know I need to use the RouterProvider component in some way, but I don't have enough experience to see the path forward. What am I missing here?
You are most probably importing useFetcher from an incorrect package. Make sure that you are importing it from #remix-run/react:
import { useFetcher } from "#remix-run/react";

Simple-react-lightbox also active on other images

I started implementing the React-simple-lightbox. It all works fine on the chosen images, but I do have another problem. I get an error on some of my articles that have an image and a title. It gives me the following error:
What's possibly the issue here?
Unhandled Runtime Error
TypeError: Cannot read properties of null (reading 'nodeName')
Call Stack
eval
node_modules/simple-react-lightbox/dist/index.es.js (14:71687)
eval
node_modules/simple-react-lightbox/dist/index.es.js (14:72063)
Array.forEach
<anonymous>
eval
node_modules/simple-react-lightbox/dist/index.es.js (14:71638)
eval
node_modules/simple-react-lightbox/dist/index.es.js (14:73845)
const PlockGrid = ({title, photoConst, photoSrc} : Props) => {
return (
<SRLWrapper options={options}>
<Plock breakpoints={breakpoints} gap="2rem">
{
[...Array(photoCount[photoConst])].map((e, i) => {
return <img
key={i + 1}
src={`${photoSrc}${i + 1}.webp`}
alt={`${title} detail`}
className="bg-gray-200 w-full hover:grayscale cursor-pointer"
loading="lazy"
/>
})
}
</Plock>
</SRLWrapper>
)
}
import { AppProps } from 'next/app'
import '../styles/index.css'
import SimpleReactLightbox from 'simple-react-lightbox'
export default function MyApp({ Component, pageProps }: AppProps) {
return <SimpleReactLightbox><Component {...pageProps} /></SimpleReactLightbox>
}
Here's the affected file with the articles as well:
import PostPreview from './post-preview'
import type Post from '../interfaces/post'
import FramerMotion from './framer-motion'
type Props = {
posts: Post[]
}
const MoreStories = ({ posts }: Props) => {
return (
<section>
<FramerMotion>
<h2 className="mb-8 text-5xl md:text-7xl font-bold tracking-tighter leading-tight">
My work
</h2>
</FramerMotion>
<div className="grid grid-cols-1 md:grid-cols-2 md:gap-x-8 xl:grid-cols-3 xl:gap-x-12 gap-y-20 mb-32">
{posts.map((post) => (
<PostPreview
key={post.slug}
title={post.title}
coverImage={post.coverImage}
date={post.date}
slug={post.slug}
/>
))}
</div>
</section>
)
}
export default MoreStories

useState hook is breaking activePost as useEffect is triggered by selectedPost

My goal was to fetch posts from Graphcms and populate an array - posts, and populate it into postlist, then the main component will change according to what the user clicks on a post from postlist, I can see the posts array is populated , but when i click on a post on postlist I get the following error
Main.js:22 Uncaught TypeError: Cannot read properties of undefined (reading 'featuredImage')
Below my files
App.js
function App() {
const [selectedPost,setSelectedPost] = useState(0);
const [posts, setPosts] = useState([]);
useEffect(() => {
const fetchPosts = async () => {
const { posts } = await request(
'https://api-ap-southeast-2.graphcms.com/v2/ckxo1np9m5kw601xpccps4lrn/master',
`
{
posts {
id
title
slug
excerpt
featuredImage
{
url
}
}
}
`
);
console.log("print posts " , posts)
setPosts(posts);
};
fetchPosts();
}, []);
return ( <div className='app'>
<Header/>
{
posts.length>0 && (<>
<Main posts={posts} selectedPost={selectedPost}/>
<PostList posts={posts} setSelectedPost={setSelectedPost} />
</>
)
}
</div>
)
}
export default App;
And the Main.js Component
const Main = ({selectedPost,posts}) => {
const[activePost,setActivePost] =useState(posts[0])
console.log("activePost ", activePost)
useEffect(()=>{
setActivePost(posts[selectedPost])
},[posts,selectedPost])
return (
<div className='main'>
<div className='mainContent'>
<div className='postHighlight'>
<div className='postContainer'>
<img
className='selectedPost'
src= {activePost.featuredImage.url}
alt=''
/>
</div>
</div>
<div className='postDetails' style={{color:'#fff'}}>
<div className='title'>
{activePost.title} </div>
<span className='itemNumber'></span>
<span className='postExcerpt'>{activePost.excerpt}</span>
</div>
<div className='otherDetails'>
</div>
</div>
</div>
)
}
export default Main
And then we have postList.js file
const PostList = ({posts,setSelectedPost}) => {
return (
<div className='postList'>
{posts.map(post=>(
<div onClick={()=>setSelectedPost(post.id)}>
<CollectionCard key={post.slug} title={post.title} excerpt={post.excerpt} imageSrc={post.featuredImage.url}/>
</div>
)) })
</div>
)
}
export default PostList
Based on your app, you are using the index of the selected post.
The error arises from your onclick function. You are passing post.id to setSelectedPost() so you are accessing the posts array incorrectly. Hence, the undefined.
Just use the current index on your map function:
<div className='postList'>
{posts.map((post, index) => (
<div onClick={() => setSelectedPost(index)} key={post.slug}>
<CollectionCard
title={post.title}
excerpt={post.excerpt}
imageSrc={post.featuredImage.url}
/>
</div>
))
}
</div>

ReactJS CORS header ‘Access-Control-Allow-Origin’ missing

I'm using a 3rd party API https://www.metaweather.com and in my package.json i've added
"proxy": "https://www.metaweather.com",
My app.js is as follows:
import { createContext, useState } from "react";
import LocationSearch from "./components/locationSearch";
import MainWeather from "./components/mainWeather";
import ExtraWeather from "./components/ExtraWeather";
export const DisplayContext = createContext({
display: false,
setDisplay: () => {},
});
function App() {
const [woeid, setWoeid] = useState(null);
const [display, setDisplay] = useState(false);
return (
<DisplayContext.Provider value={{ display, setDisplay }}>
<LocationSearch setWoeid={setWoeid} />
<MainWeather woeid={woeid} />
<ExtraWeather />
</DisplayContext.Provider>
);
}
export default App;
my LocationSearch.jsx:
import React, { useContext, useState } from "react";
import axios from "axios";
import { DisplayContext } from "../App";
const LocationSearch = ({ setWoeid }) => {
const [data, setData] = useState({
location: "",
});
const { setDisplay } = useContext(DisplayContext);
function submit(e) {
e.preventDefault();
axios
.get(
// "https://cors-anywhere.herokuapp.com/https://www.metaweather.com/api/location/search/?query=" +
"/api/location/search/?query=" +
data.location,
{
location: data.location,
}
)
.then((res) => {
console.log(res.data[0].woeid);
setWoeid(res.data[0].woeid);
setTimeout(() => setDisplay(true), 5000);
})
.catch((err) => {
console.log(err);
});
}
function handle(e) {
const newdata = { ...data };
newdata[e.target.id] = e.target.value;
setData(newdata);
console.log(newdata);
}
return (
<div className="flex w-96 mx-auto mt-5 p-3 rounded-xl bg-blue-300">
<form className="flex w-96 mx-auto p-3 rounded-xl bg-white">
<div>
<input
className="text-gray-700"
onChange={(e) => handle(e)}
id="location"
value={data.location}
placeholder="Search for location"
type="text"
/>
<button
className="bg-blue-900 text-gray-300 py-3 px-5 ml-12 rounded-xl"
type="submit"
onClick={(e) => submit(e)}
>
Search
</button>
</div>
</form>
</div>
);
};
export default LocationSearch;
my MainWeather.jsx:
import React, { useContext, useEffect, useState } from "react";
import axios from "axios";
import { DisplayContext } from "../App";
import Loader from "react-loader-spinner";
const MainWeather = ({ woeid }) => {
const [temp, setTemp] = useState([]);
const [icon, setIcon] = useState("");
const { display } = useContext(DisplayContext);
const [load, setLoad] = useState(false);
useEffect(() => {
axios
.get(
// "https://cors-anywhere.herokuapp.com/https://www.metaweather.com/api/location/" +
"/api/location/" +
woeid
)
.then((res) => {
setLoad(true);
console.log(res.data[0]);
setIcon(res.data.consolidated_weather[0].weather_state_abbr);
setTemp((prev) => {
return [
...prev,
res.data.consolidated_weather[0].the_temp,
res.data.consolidated_weather[0].min_temp,
res.data.consolidated_weather[0].max_temp,
res.data.consolidated_weather[0].weather_state_name,
];
});
})
.catch((err) => {
console.log(err);
});
}, [woeid]);
return (
<>
{display && (
<div className="w-96 flex flex-col mx-auto p-3 mt-2 rounded-xl bg-blue-300">
<img
src={"/static/img/weather/" + icon + ".svg"}
alt="Current weather icon"
className="w-40 mx-auto pb-4"
/>
<p className="mx-auto text-5xl pb-3">{Math.round(temp[0])}°C</p>
<p className="mx-auto pb-1">
{Math.round(temp[1])} / {Math.round(temp[2])}
</p>
<p className="mx-auto pb-2">{temp[3]}</p>
</div>
)}
{!display && (
<div>
{load && (
<div className="flex w-96 h-80 mx-auto mt-5 p-3 rounded-xl bg-blue-300">
<Loader
className="m-auto"
type="Puff"
color="#00BFFF"
height={100}
width={100}
timeout={5000}
/>
</div>
)}
{!load && (
<div className="flex w-96 h-80 mx-auto mt-5 p-3 rounded-xl bg-blue-300">
<h1 className="m-auto">Please enter a location</h1>
</div>
)}
</div>
)}
</>
);
};
export default MainWeather;
The ExtraWeather.jsx isn't relevant.
If I comment out the MainWeather and log the return from the LocationSearch it returns to object perfectly but as soon as I introduce the MainWeather back I get "CORS header ‘Access-Control-Allow-Origin’ missing" error. I've tried everything I can find from hosting the app on Netlify, changing what is the proxy to the local host address, moving things to different places, and I'm unsure if I did it correctly but I did try a reverse proxy.
Also using herokuapp and a browser extension does fix the problem but I want something more permanent.
Any help will be greatly appreciated.
The issue is that the response is being redirected to include a / suffix, ie
HTTP/2 301
location: https://www.metaweather.com/api/location/44418/
This causes your browser to re-attempt the request to that URL which bypasses your proxy.
Try including the / suffix, eg
axios.get(`/api/location/${woeid}/`)
Keep in mind that the proxy setting only works for local development. If you're deploying to Netlify, see https://docs.netlify.com/routing/redirects/rewrites-proxies/#proxy-to-another-service
Debugging Process
Something was directing your browser to try and access the API by its full URL so I suspected a redirect.
I just ran
curl -v "https://www.metaweather.com/api/location/44418" -o /dev/null
and looked at the response status and headers...
> GET /api/location/44418 HTTP/2
> Host: www.metaweather.com
< HTTP/2 301
< location: https://www.metaweather.com/api/location/44418/
Spotting the difference was the hard part 😄
You could probably have seen something similar in your browser dev-tools Network panel; first a request to /api/location/44418 with a 301 response and location header, then a request to https://www.metaweather.com/api/location/44418/ which failed CORS checks

TypeError: Cannot read property after refresh or after manipulation

I am getting the following error after a refresh and sometimes on initial project run:
TypeError: Cannot read property 'statements' of undefined
This is really confusing because the data is rendering fine it just seems the connection fails. Any idea what may be causing the error?
There are no additional errors when I remove the statements.map
I used this medium article to get everything up and running: Medium Article
index.js:
import React, { useState, useReducer } from 'react'
import { useQuery } from '#apollo/react-hooks'
// Custom layout
import Layout from "../components/layout"
import '../sass/styles.scss'
// Data Query
import STATEMENTS_QUERY from '../graphql/statements'
function StatementCall(context) {
const { loading, error, data } = useQuery(STATEMENTS_QUERY, {});
return (
<Layout>
<div className="container">
<div className="row spaces">
<div className="col-md-12">
<p>Testing</p>
{data.statements.data.map((statement, index) => (
<div>
<p>{statement.id}</p>
<p>{statement.title}</p>
</div>
))}
</div>
</div>
</div>
</Layout>
)
}
export default StatementCall
graphql/statements.js:
import gql from 'graphql-tag';
const STATEMENTS_QUERY = gql`
query {
statements(filter: {destination: 1991}) {
data {
id
title
body
}
}
}
`;
export default STATEMENTS_QUERY;
You could check if the results are loading, before you try to render them.
Example:
function StatementCall(context) {
const { loading, error, data } = useQuery(STATEMENTS_QUERY, {});
if (loading) {
return <p>Loading...</p>;
}
if (error) {
// Handle error?
return <p>{error}</p>;
}
return (
<Layout>
<div className="container">
<div className="row spaces">
<div className="col-md-12">
<p>Testing</p>
{data.statements.data.map((statement, index) => (
<div>
<p>{statement.id}</p>
<p>{statement.title}</p>
</div>
))}
</div>
</div>
</div>
</Layout>
)
}
Check the example in useQuery docs.

Categories