I tried to implement a SSG application in Amplify with Next.js following this tutorial
pages/index.tsx worked well. But pages/posts/[id].tsx doesn't work and showed the following error.
Server Error
Error: No current user
This error happened while generating the page. Any console logs will be displayed in the terminal window.
And also the console showed this error.
GET http://localhost:3000/posts/da53d1f3-775f-49c4-a311-d311f2e5623f 500 (Internal Server Error)
My code in pages/posts/[id].tsx is following.
May I have your idea how to resolve this error?
import { Amplify, API, withSSRContext } from "aws-amplify";
import Head from "next/head";
import { useRouter } from "next/router";
import awsExports from "../../aws-exports";
import { getPost, listPosts } from "../../graphql/queries";
Amplify.configure({ ...awsExports, ssr: true });
export async function getStaticPaths() {
const SSR = withSSRContext();
const { data } = await SSR.API.graphql({ query: listPosts });
const paths = data.listPosts.items.map((post: any) => ({
params: { id: post.id },
}));
return {
fallback: true,
paths,
};
}
export async function getStaticProps({ params }: { params: any }) {
const SSR = withSSRContext();
const { data } = await SSR.API.graphql({
query: getPost,
variables: {
id: params.id,
},
});
return {
props: {
post: data.getPost,
},
};
}
export default function Post({ post }: { post: any }) {
const router = useRouter();
if (router.isFallback) {
return (
<div>
<h1>Loading…</h1>
</div>
);
}
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}
Related
React Testing Using Mock service worker is returning undefined and then taking values from the actual API. As you can see from the image down below the test is getting passed by getting values from the actual API and the name ARLO WANG is getting pulled out. Where as the name I have kept in the mockResponse is "first last" in the handler.js file.
FollowersList.js
import React, { useEffect, useState } from 'react'
import "./FollowersList.css"
import axios from "axios"
import { Link } from 'react-router-dom';
import { v4 } from 'uuid';
export default function FollowersList() {
const [followers, setFollowers] = useState([]);
useEffect(() => {
fetchFollowers()
}, []);
const fetchFollowers = async () => {
const {data} = await axios.get("https://randomuser.me/api/?results=5")
setFollowers(data.results)
}
// console.log(followers)
return (
<div className="followerslist-container">
<div>
{followers.map((follower, index) => (
<div className="follower-item" key={v4()} data-testid={`follower-item-${index}`}>
<div className="followers-details">
<div className="follower-item-name">
<h4>{follower.name.first}</h4> <h4>{follower.name.last}</h4>
</div>
<p>{follower.login.username}</p>
</div>
</div>
))}
</div>
<div className="todo-footer">
<Link to="/">Go Back</Link>
</div>
</div>
)
}
FollowersList.test.js
import { render, screen } from "#testing-library/react";
import { BrowserRouter } from "react-router-dom";
import FollowersList from "../FollowersList";
const MockFollowersList = () => {
return (
<BrowserRouter>
<FollowersList />
</BrowserRouter>
);
};
describe("FollowersList Component", () => {
test("renders first follower", async () => {
render(<MockFollowersList />);
screen.debug()
expect(await screen.findByTestId("follower-item-0")).toBeInTheDocument();
});
});
src/mock/handler.js
import { rest } from 'msw';
const mockResponse = {
data: {
results: [
{
name: {
first: "first",
last: "last",
},
login: {
username: "x",
},
},
],
},
};
export const handlers = [
rest.get('https://randomuser.me/api/', (req, res, ctx) => {
return res(ctx.json({mockResponse}))
}
})
]
VSCODE terminal
Something is going wrong in the return line in handlers array in the handler file. It's not sending back the mockResponse correctly.
Found the mistake. The response structure returned by the actual API and the msw is different. Just had to remove the 'data' object in the mockResponse and keep the it just as an array of 'results'.
import { rest } from 'msw';
const mockResponse = {
results: [
{
name: {
first: "first",
last: "last",
},
login: {
username: "x",
},
},
],
};
export const handlers = [
rest.get('https://randomuser.me/api/', (req, res, ctx) => {
return res(ctx.json(mockResponse))
}
})
]
Get post by ID ( slug ) from prisma when getStaticProps() before page build
So the issue is that I cannot use React hook in getStaticProps. I was going to get slug names with useRouter, then query for post by using the slug (postID), but I learned that I cannot run prisma inside of body components. Then I learned that I can use getStaticProps and getStaticPaths to query the post by its ID before build time.
How do I get N levels of slug names in getStaticProps?.
Code
/post/[...slugs].tsx
My url looks like: localhost:3000/post/postID/PostTitle
such as localhost:3000/post/b513-ad29e3cc67d9/Post%20Title
import { Post, PrismaClient } from '#prisma/client';
import { GetStaticPaths, GetStaticProps } from 'next';
import { useRouter } from 'next/router';
type postByIdProps = {
postById: Post
}
export default function PostDetail({postById}: postByIdProps) {
return (
<>
<div>
{postById.title}
</div>
</>
);
}
export const getStaticProps = async(context: any)=>{
// I can't ues React Hook here, but I don't know how to get slug name without the hook.
const router = useRouter();
const slugs: any = router.query.slugs;
const postId = slugs?.[0].toString()
//Prisma
const prisma = new PrismaClient()
const postById = prisma.post.findUnique({
where: {
postId: postId,
},
})
return postById
}
export const getStaticPaths: GetStaticPaths<{ slug: string }> = async () => {
return {
paths: [], //indicates that no page needs be created at build time
fallback: 'blocking' //indicates the type of fallback
}
}
This worked fro me, but if someone can improve this code, more than welcome.
How to Build a Fullstack App with Next.js, Prisma, and PostgreSQL
code
import { Post } from '#prisma/client';
import { GetStaticPaths, GetStaticProps } from 'next';
import prisma from '../api/prisma';
type postByIdProps = {
post: Post
}
export default function PostDetail({post}: postByIdProps) {
console.log("Post here,", post)
return (
<>
<div>
{post.title}
</div>
</>
);
}
export const getStaticProps = async({params}: any)=>{
const postId = params.slugs[0] //gets post's ID
const post = await prisma.post.findUnique({
where:{
postId: String(postId)
},
})
return {
props:{
post
}
}
}
export const getStaticPaths: GetStaticPaths<{ slug: string }> = async () => {
return {
paths: [], //indicates that no page needs be created at build time
fallback: 'blocking' //indicates the type of fallback
}
}
Below is the code located at "Pages/home.js". // localhost:3000/home
import axios from 'axios';
import Section1 from '../components/home-sections/section-1';
const Homepage = ({ show }) => {
const Html = JSON.parse(show.response.DesktopHTML);
const renderSection = () => {
return Html.map((itemData,index)=>{
return(<div key={index}>{itemData.DisplayName}</div>)
})
}
return(
<div>
{ renderSection()}
<Section1 />
</div>
)
}
export const getServerSideProps = async ({ query }) => {
try {
const response = await axios.get(
`https://api.example.com/getHomeSection?title=Section 1`
);
return {
props: {
show: response.data,
},
};
} catch (error) {
return {
props: {
error: error.error,
},
};
}
};
export default Homepage;
Now same code I added into section-1.js and this file is located to "components/home-sections/section-1.js"
Now getServerSideProps is working fine in home.js, but in section-1.js it is not working.
Error: TypeError: show is undefined in section-1.js
You cannot use getServerSideProps in non-page components. You can either pass the prop from Home to HomeSection or create a context so the value can be available globally from the component tree
getServerSideProps can only be exported from a page. You can’t export
it from non-page files.
https://nextjs.org/docs/basic-features/data-fetching#only-allowed-in-a-page-2
getServerSideProps can only be exported from Page components. It will not be run on components imported into a page.
However, you could export a function from the component that returns the props, and call that function from the page's getServerSideProps function.
Create a getServerSideProps function on the component.
// #components/MyComponent.tsx
import { GetServerSidePropsContext } from 'next';
function MyComponent(props: IMyComponentProps) {
return (<div>MyComponent</div>;)
}
MyComponent.getServerSideProps = async (context: GetServerSidePropsContext): Promise<{ props: IMyComponentProps }> => {
return { props: { ... } };
}
export default MyComponent;
In your page's getServerSideProps function, call the component's getServerSideProps function and merge the props from the component with the props from the page.
// mypage.tsx
import MyComponent from '#components/MyComponent';
const Page: NextPageWithLayout = (props: IIndexPageProps) => {
return <MyComponent />;
}
export async function getServerSideProps(context: GetServerSidePropsContext): Promise<{ props: IIndexPageProps }> {
let componentServerSideProps = await MyComponent.getServerSideProps(context);
let otherServerSideProps = { props: { ... } };
return {
props: {
...componentServerSideProps.props,
...otherServerSideProps.props
}
};
}
Blog I followed : https://medium.com/#sarafathulla/how-to-add-firebase-push-notifications-in-next-js-react-8eecc56b5cab
I did the exact same process, but I cant see notifications in my console or anywhere.
It was not mentioned in which file we have to do this part so I did in my _app.js in pages folder:
import "../styles/globals.css";
import { firebaseCloudMessaging } from "../webPush";
import "firebase/messaging";
import firebase from "firebase/app";
import "react-quill/dist/quill.snow.css";
function MyApp({ Component, pageProps }) {
React.useEffect(() => {
setToken();
async function setToken() {
try {
const token = await firebaseCloudMessaging.init();
if (token) {
console.log("checking token");
getMessage();
}
} catch (error) {
console.log(error);
}
}
function getMessage() {
const messaging = firebase.messaging();
messaging.onMessage((message) => console.log("foreground ", message));
}
},[]);
React.useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return <Component {...pageProps} />;
}
export default MyApp;
Rest of the files are exactly the same as mentioned in the blog. There are no errors in console. When I try to send POST from postman I receive the following:
{
"multicast_id": 5967494073963735539,
"success": 1,
"failure": 0,
"canonical_ids": 0,
"results": [
{
"message_id": "0:1601972416385592%e609af1cf9fd7ecd"
}
]
}
Can someone help me out here? I can't figure out what I'm doing wrong.
Studying Vuex. I wrote a simple login page against the example project and the document, but when I tried to use a action function, the developer tool just warned me
Here is my code:
src/views/Login.vue
handleLogin (formName) {
this.$refs[formName].validate(valid => {
if (valid) {
// to do
this.$store.dispatch('user/login', this.loginUser)
} else {
......
})
}
})
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/User/user'
// import info from './modules/info'
Vue.use(Vuex)
export default new Vuex.Store({
strict: false,
modules: {
user,
// info
}
})
/src/store/modules/User/actions.js
export const userActions = {
login({commit}, loginUser) {
commit(LOGIN)
axios.post(`${ API_BASE_USER }/login`, loginUser)
.then(res => {
console.log(res)
if (res.status == 200) { commit(LOGIN_SUCCESS, res.data) }
else { commit(LOGIN_FAILURE, res.data) }
})
}
}
/src/store/modules/User/user.js
import { userActions } from './actions'
import { userMutations } from './mutations'
export default {
namespaced: true,
state: {
token: ''
},
actions: Object.assign({}, userActions),
mutations: Object.assign({}, userMutations)
}
I got it.
The origin mutations-type.js export const LOGIN = LOGIN
But the correct mutation-type.js should be export const LOGIN = 'LOGIN'
This can also happen when you call $store.commit() without providing it an argument
Had a similar situation, where the Mutation name started with a CAP (), but the actual mutation started with a non cap():
RetrieveContractorSuccess: 'retrieveContractorSuccess'
(before was RetrieveContractorSuccess: 'RetrieveContractorSuccess')