How to update page when data is changed in nextjs? - next.js

When I delete one of the notes, it deletes from the DB. And to see the effect, I need to reload the page every time I delete a note.
How do I see the not deleted notes without reloading the page?
Here's the code for my page:
export default function Home(notes) {
const [notesData, setNotesData] = useState(notes);
const deleteNote = async (note) => {
const res = await fetch(`http://localhost:3000/api/${note}`, {
method: "DELETE",
});
};
return (
<div>
<h1>Notes:</h1>
{notesData.notes.map((note) => {
return (
<div className="flex">
<p>{note.title}</p>
<p onClick={() => deleteNote(note.title)}>Delete</p>
</div>
);
})}
</div>
);
}
export async function getServerSideProps() {
const res = await fetch(`http://localhost:3000/api`);
const { data } = await res.json();
return { props: { notes: data } };
}

If you're fetching the data with getServerSideProps you need to recall that in order to get the updated data like this :
import { useRouter } from 'next/router';
const router = useRouter()
const refreshData = () => router.replace(router.asPath);
But also you can store the data from getServerSideProps in a state and render that state and trigger a state update after a note is deleted like this :
export default function Home(notes) {
const [notesData, setNotesData] = useState(notes);
const deleteNote = async (note) => {
const res = await fetch(`http://localhost:3000/api/${note}`, {
method: "DELETE",
});
};
return (
<div>
<h1>Notes:</h1>
{notesData.notes.map((note) => {
return (
<div className="flex">
<p>{note.title}</p>
<p onClick={() => deleteNote(note.title).then(()=>{
const res = await fetch(`http://localhost:3000/api`);
const { data } = await res.json();
setNotesData(data)
})
}>Delete</p>
</div>
);
})}
</div>
);
}
export async function getServerSideProps() {
const res = await fetch(`http://localhost:3000/api`);
const { data } = await res.json();
return { props: { notes: data } };
}

Related

Next.js reference error "client" is not defined

I am having an error when trying to fetch product data from sanity with getStaticPaths
here is my code:
import React, { useState } from "react";
function ProductPage({ product, products }) {
const { image, name, details, price } = product;
return (
<div className="pdPage">
<div className="container">{name}</div>
</div>
);
}
export default ProductPage;
export const getStaticPaths = async () => {
const query = `*[_type == "product"] {
slug {
current
}
}
`;
const products = await client.fetch(query);
const paths = products.map((product) => ({
params: {
slug: product.slug.current,
},
}));
return {
paths,
fallback: "blocking",
};
};
export const getStaticProps = async ({ params: { slug } }) => {
const query = `*[_type == "product" && slug.current == '${slug}'][0]`;
const productsQuery = '*[_type == "product"]';
const product = await client.fetch(query);
const products = await client.fetch(productsQuery);
console.log(product);
return {
props: { products, product },
};
};
And then I get reference error client is not defined from getstatic client.fetch
I even delete my code and replace from tutor github repository, but get the same error
After figure it out sometimes I found that I forgot to import client from

Solution to prefetch in a component on nextjs

I'm looking for a solution/module where I don't need to inject inital/fallback data for swr/react-query things from getServerSideProps. Like...
from
// fetcher.ts
export default fetcher = async (url: string) => {
return await fetch(url)
.then(res => res.json())
}
// getUserData.ts
export default function getUserData() {
return fetcher('/api')
}
// index.tsx
const Page = (props: {
// I know this typing doesn't work, only to deliver my intention
userData: Awaited<ReturnType<typeof getServerSideProps>>['props']
}) => {
const { data } = useSWR('/api', fetcher, {
fallbackData: props.userData,
})
// ...SSR with data...
}
export const getServerSideProps = async (ctx: ...) => {
const userData = await getUserData()
return {
props: {
userData,
},
}
}
to
// useUserData.ts
const fetcher = async (url: string) => {
return await fetch(url)
.then(res => res.json())
};
const url = '/api';
function useUserData() {
let fallbackData: Awaited<ReturnType<typeof fetcher>>;
if (typeof window === 'undefined') {
fallbackData = await fetcher(url);
}
const data = useSWR(
url,
fetcher,
{
fallbackData: fallbackData!,
}
);
return data;
}
// index.tsx
const Page = () => {
const data = useUserData()
// ...SSR with data...
}
My goal is making things related to userData modularized into a component.

Dynamic Content not loading in NextJs Dynamic Pages

Currently I have a Blogs collection type in my Strapi CMS with an Id and title data fields. I'm using NextJs for my frontend to dynamically load blog content for each blog page. But my content doesn't load when my dynamic page is loaded.
Page where individual blogs are stored:
{posts &&
posts.map((item, idx) => (
<Link href={`/BlogPage/${item.id}`}>
<div>
<img src={`http://localhost:1337${item.Thumbnail.url}`}/>
</div>
</Link>
Then inside my BlogPage directory i have a file [id].js:
export default function name({blog}) {
return (
<>
<div>
{blog.Title}
</div>
</>
)}
// Tell nextjs how many pages are there
export async function getStaticPaths() {
const res = await fetch("http://localhost:1337/blogs");
const posts = await res.json();
const paths = posts.map((blog) => ({
params: { id: blog.id.toString() },
}));
return {
paths,
fallback: false,
};
}
// Get data for each individual page
export async function getStaticProps({ params }) {
const { id } = params;
const res = await fetch(`http://localhost:1337/blogs?id=${id}`);
const data = await res.json();
const posts = data[0];
return {
props: { posts },
};
}
This takes me to this URL http://localhost:3000/BlogPage/1 and gives me an error
TypeError: Cannot read property 'Title' of undefined
Try to out the getStaticProps and getStaticPaths of name function
export async function getStaticPaths() {
const res = await fetch("http://localhost:1337/blogs");
const posts = await res.json();
const paths = posts.map((blog) => ({
params: { id: blog.id.toString() },
}));
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
const { id } = params;
const res = await fetch(`http://localhost:1337/blogs?id=${id}`);
const data = await res.json();
const posts = data[0];
return {
props: { posts },
};
}
export default function name({posts }) { // change this line
return (
<>
<div>
{posts.Title} // change this line // Are you sure is it Title? not title? if it is with lowercase, it will return null
</div>
</>
)
}

too many renders react hooks, useEffect, map

hey guys I'm trying to add to sum a simple product price for each iteration and notice its runs too manytimes. in the use effect I have to listen to the UserCart. can't do it with an empty array/[userCart].
also I get the following error in more components which i couldnt fix for the same reason with the last argument in the useEffect:
update: I did divide the useEffect as suggusted but it with [userCart ] it enter infinite loop
the code:
import React, { useState, useEffect } from 'react'
import firebase from 'firebase';
import { useAuth, useStoreUpdate } from '../contexts/FirebaseContext';
export default function Cart() {
const [userMail, setUserMail] = useState(undefined)
const [userCart, setUserCart] = useState(undefined)
const [totalAmmout, setTotalAmmout] = useState(0)
const user = useAuth()
const userDoc = firebase.firestore().collection("cart").doc(userMail)
const updateStore = useStoreUpdate()
const updateCart = () => {
userDoc.get().then((doc) => {
if (doc.exists) {
let cart = doc.data()
setUserCart(cart)
}
})
}
async function removeFromCart(itemId, name, url, price, category, type, description) {
const cartItem = { itemId, name, url, price, category, type, description }
await userDoc.update({
item: firebase.firestore.FieldValue.arrayRemove(cartItem)
})
await updateCart()
await updateStore()
}
useEffect(() => {
if (user.currentUser) {
setUserMail(user.currentUser.email)
updateCart()
updateStore()
}
},userCart)
if (!userCart) return <h1>hold</h1>
let total = 0
return (
<main className="main-cart">
<div className="container">
{userCart.item && userCart.item.length >= 1 && userCart.item.map((item, i, arr) => {
console.log(item); //it runs more than 20 times
return (
< div className="item-container" key={item.itemId} >
<h3>{item.name}</h3>
<p>{item.price}</p>
<img height="150px" width="150px" src={item.url} alt="" />
<button onClick={async () => {
await removeFromCart(item.itemId, item.name, item.url, item.price, item.category, item.type, item.description)
}}>X</button>
</div>
)
})}
</div>
<div className="fixed-bottom-link">
<button>finish purchase</button>
</div>
</main >
)
}
edit :
I found the major component, but still couldnt make it work
the 2nd useEffect [userCart] wont run, it works only when not in the array, but it enter a loop:
import React, { useContext, useState, useEffect } from 'react';
import { auth } from '../firebase/firebase';
import firebase from '../firebase/firebase';
const FirebaseContext = React.createContext()
const StoreContext = React.createContext()
const StoreContextUpdate = React.createContext()
export function useAuth() {
return useContext(FirebaseContext)
}
export function useStore() {
return useContext(StoreContext)
}
export function useStoreUpdate() {
return useContext(StoreContextUpdate)
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true)
const [userMail, setUserMail] = useState(undefined)
const [userCart, setUserCart] = useState(undefined)
const userDoc = firebase.firestore().collection("cart").doc(userMail)
const updateCart = () => {
userDoc.get().then((doc) => {
if (doc.exists) {
let cart = doc.data()
setUserCart(cart)
}
})
}
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email, password)
}
function login(email, pasword) {
return auth.signInWithEmailAndPassword(email, pasword)
}
function logout() {
return auth.signOut()
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async user => {
await setCurrentUser(user)
await setLoading(false)
})
return unsubscribe
}, [userCart])
useEffect(()=>{
async function refreshCart(){
await setUserMail(currentUser.email);
await updateCart()
}
if (currentUser && currentUser.email) {
refreshCart()
}
return
},[userCart])
const value = {
currentUser,
signup,
login,
logout
}
return (
<FirebaseContext.Provider value={value}>
<StoreContext.Provider value={userCart && userCart.item}>
<StoreContextUpdate.Provider value={updateCart}>
{!loading && children}
</StoreContextUpdate.Provider>
</StoreContext.Provider>
</FirebaseContext.Provider>
)
}
useEffect(() => {
if (user.currentUser) {
setUserMail(user.currentUser.email);
updateCart();
updateStore();
}
}, [userCart]); //<-- userCart needs to be inside array
and the reason for too many rerenders is you're calling updateCart function when the userCart changes and then userCart change will cause the call of updateCart again and this loop will repeat itself until you're out of memory
I think adding another useEffect might help you
useEffect(() => {
if (user.currentUser) {
setUserMail(user.currentUser.email);
updateStore();
}
}, [userCart]); //<-- userCart needs to be inside array
useEffect(() => {
if (user.currentUser) {
updateCart();
}
}, []);

Redux actions without return or dispatch

I am implementing Oauth from google with redux, and I wanted to have all google API calls handled from my redux and ended up writing helper functions in my actions file that doesn't return anything or call dispatch. I ended up with code where I only dispatch once from my JSX file and wondering if this is okay or there is another better way to do it?
The code is as follows:
authActions.js
const clientId = process.env.REACT_APP_GOOGLE_OAUTH_KEY;
let auth;
export const authInit = () => (dispatch) => {
window.gapi.load('client:auth2', () =>
window.gapi.client.init({ clientId, scope: 'email' }).then(() => {
auth = window.gapi.auth2.getAuthInstance();
dispatch(changeSignedIn(auth.isSignedIn.get()));
auth.isSignedIn.listen((signedIn) => dispatch(changeSignedIn(signedIn)));
})
);
};
export const signIn = () => {
auth.signIn();
};
export const signOut = () => {
auth.signOut();
};
export const changeSignedIn = (signedIn) => {
const userId = signedIn ? auth.currentUser.get().getId() : null;
return {
type: SIGN_CHANGE,
payload: { signedIn, userId },
};
};
GoogleAuth.jsx
import { useSelector, useDispatch } from 'react-redux';
import classNames from 'classnames';
import { authInit, signIn, signOut } from '../../actions/authActions';
function GoogleAuth() {
const { signedIn } = useSelector((state) => state.auth);
const dispatch = useDispatch();
useEffect(() => {
dispatch(authInit());
}, [dispatch]);
const onClick = () => {
if (signedIn) {
signOut();
} else {
signIn();
}
};
let content;
if (signedIn === null) {
return null;
} else if (signedIn) {
content = 'Sign Out';
} else {
content = 'Sign In';
}
return (
<div className="item">
<button
className={classNames('ui google button', {
green: !signedIn,
red: signedIn,
})}
onClick={onClick}
>
<i className="ui icon google" />
{content}
</button>
</div>
);
}
export default GoogleAuth;
The code works fine, but it feels like it might be misleading having action calls in JSX but not dispatching it, is it okay?

Resources