Sanity doesnt connect blog post? - reactjs

i have an issue with sanity, but i think the problem is the code doesnt fetch the blog post. any idea?
import { useState, useEffect } from "react"
import { Link, useParams } from "react-router-dom"
import client from "../client"
import { PortableText } from '#portabletext/react'
export default function SinglePost() {
const [singlePost, setSinglePost] = useState([])
const [isLoading, setIsLoading] = useState(true)
const { slug } = useParams()
useEffect(() => {
client
.fetch(
`*[slug.current == "${slug}"] {
title,
body,
mainImage {
asset -> {
_id,
url
},
alt
}
}`
)
.then((data) => setSinglePost(data[0]))
setIsLoading(false)
}, [slug])
return (
<>
{isLoading ? (
Loading...
) : (
{singlePost.title}
{singlePost.mainImage && singlePost.mainImage.asset && (
)}
<div className="block__content">
<PortableText
value={singlePost.body}
projectId="*******"
dataset="production"
/>
</div>
<button>
<Link
to="/blog"
className="py-2 px-6 rounded shadow text-white bg-black hover:bg-transparent border-2 border-black transition-all duration-500 hover:text-black font-bold"
>
Read more articles
</Link>
</button>
</section>
)}
</>
)
}

Related

how to map and display a set of object from a data collection from mongodb in nextjs

I am currently trying to filter products of the same category and display on the page when the user clicks on a particular category. I've done this with the product detail page but I can't seem to figure out the right way to pull out all categories on the category page. Nothing expept the layout displays on the category page when redirected. Please help me using the following code
/pages/category/[slug].js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { useContext } from 'react';
import { toast } from 'react-toastify';
import Layout from '../../components/Layout';
import ProductItem from '../../components/ProductItem';
import Product from '../../components/models/Product';
import db from '../../utils/db';
import { Store } from '../../utils/Store';
import { useRouter } from 'next/router';
export default function Home({ products }) {
const router = useRouter();
const { state, dispatch } = useContext(Store);
const { cart } = state;
const addToCartHandler = async (product) => {
const existItem = cart.cartItems.find((x) => x.slug === product.slug);
const quantity = existItem ? existItem.quantity + 1 : 1;
const { data } = await axios.get(`/api/products/${product._id}`);
if (data.countInStock < quantity) {
return toast.error('Sorry. Product is out of stock');
}
dispatch({ type: 'CART_ADD_ITEM', payload: { ...product, quantity } });
toast.success('Product added to the cart');
};
const buyNowHandler = async (product) => {
const existItem = cart.cartItems.find((x) => x.slug === product.slug);
const quantity = existItem ? existItem.quantity + 1 : 1;
const { data } = await axios.get(`/api/products/${product._id}`);
if (data.countInStock < quantity) {
return toast.error('Sorry. Product is out of stock');
}
dispatch({ type: 'CART_ADD_ITEM', payload: { ...product, quantity } });
toast.success('Product added to the cart');
router.push('/cart');
};
return (
<Layout title="Home Page">
<div className="products-heading">
<h2>Best Selling Products</h2>
<p>Smoke accessories of many variations</p>
</div>
<div className="products-container">
{products.map((item, index) => (
<ProductItem
product={item}
key={index}
addToCartHandler={addToCartHandler}
buyNowHandler={buyNowHandler}
></ProductItem>
))}
</div>
</Layout>
);
}
export async function getServerSideProps(params) {
const { category } = params;
await db.connect();
const products = await Product.find().lean();
return {
props: {
products: products.filter((productItem) =>
productItem.category.includes(category)
),
},
};
}
/components/Layout.jsx
import React, { useContext, useEffect, useState } from 'react';
import Head from 'next/head';
import Link from 'next/link';
import { Menu } from '#headlessui/react';
import Category from './Category';
export default function Layout() {
return (
<>
<Head>
<title>
{title ? title + ' - Puffizzy' : 'Puffizzy Accessories'}
</title>
<meta name="description" content="Puffizzy Ecommerce Website" />
<link rel="icon" href="/puffizzy_logo.png" />
</Head>
<ToastContainer position="bottom-center" limit={1} />
<div className="flex min-h-screen flex-col justify-between">
<header>
<nav className="flex h-24 justify-between item-center px-4 py-4">
<Link href="/">
<img src="/puffizzy_logo_name.png" alt="" className="w-24" />
</Link>
<div className="navbar-right">
<div className="categories">
<Menu as="div" className="relative inline-block z-10 bg-white">
<Menu.Button className="">Categories</Menu.Button>
<Menu.Items className="absolute right-0 w-full origin-top-right shadow-lg">
<Menu.Item>
<Category />
</Menu.Item>
</Menu.Items>
</Menu>
</div>
</div>
</nav>
</header>
<main className="container m-auto mt-4 px-4">{children}</main>
<footer className="flex h-10 justify-center items-center shadow-inner">
<p>Footer</p>
</footer>
</div>
</>
);
}
/componenets/Category.jsx
import Link from 'next/link';
import React from 'react';
export default function Category() {
return (
<div className="bg-gray-200">
{['Lighter', 'Crusher', 'Pipe', 'Roller', 'Hookah', 'All'].map(
(item, index) => (
<div
key={index}
className="flex p-2 hover:bg-gray-100"
>
<Link href={`/category/${item}`}>{item}</Link>
</div>
)
)}
</div>
);
}

NextJs router seems very slow

The problem is about that is taking long time to response another route. It seems next router's error and it can cause by this ("getServerSideProps"). How can I change with this code to another or did I need to change react router instead? This is index which used ("getServerSideProps") -
import axios from 'axios';
import VideoCard from '../components/VideoCard';
import { BASE_URL } from '../utils';
import { Video } from '../types';
import NoResults from '../components/NoResults';
interface IProps {
videos: Video[];
}
const Home = ({ videos }: IProps) => {
return (
<div className='flex flex-col gap-10 videos h-full'>
{videos.length
? videos?.map((video: Video) => (
<VideoCard post={video} isShowingOnHome key={video._id} />
))
: <NoResults text={`No Videos`} />}
</div>
);
};
export default Home;
export const getServerSideProps = async ({
query: { topic },
}: {
query: { topic: string };
}) => {
let response = await axios.get(`${BASE_URL}/api/post`);
if(topic) {
response = await axios.get(`${BASE_URL}/api/discover/${topic}`);
}
return {
props: { videos: response.data },
};
};
This is from another code with that used next router
import React, { useState } from 'react';
import { NextPage } from 'next';
import { useRouter } from 'next/router';
import Link from 'next/link';
import { AiFillHome, AiOutlineMenu } from 'react-icons/ai';
import { ImCancelCircle } from 'react-icons/im';
import SuggestedAccounts from './SuggestedAccounts';
import Discover from './Discover';
import Footer from './Footer';
import useAuthStore from '../store/authStore';
const Sidebar: NextPage = () => {
const [showSidebar, setShowSidebar] = useState<Boolean>(true);
const { pathname } = useRouter();
const { fetchAllUsers, allUsers }: any = useAuthStore();
const activeLink = 'flex items-center gap-3 hover:bg-primary p-3 justify-center xl:justify-start cursor-pointer font-semibold text-[#F51997] rounded';
const normalLink = 'flex items-center gap-3 hover:bg-primary p-3 justify-center xl:justify-start cursor-pointer font-semibold rounded';
return (
<div>
<div
className='block xl:hidden m-2 ml-4 mt-3 text-xl'
onClick={() => setShowSidebar(!showSidebar)}
>
{showSidebar ? <ImCancelCircle /> : <AiOutlineMenu />}
</div>
{showSidebar && (
<div className='xl:w-400 w-20 flex flex-col justify-start mb-10 border-r-2 border-gray-100 xl:border-0 p-3 '>
<div className='xl:border-b-2 border-gray-200 xl:pb-4'>
<Link href='/'>
<div className={pathname === '/' ? activeLink : normalLink}>
<p className='text-2xl'>
<AiFillHome />
</p>
<span className='capitalize text-xl hidden xl:block'>
For You
</span>
</div>
</Link>
</div>
<Discover />
<SuggestedAccounts
fetchAllUsers={fetchAllUsers}
allUsers={allUsers}
/>
<Footer />
</div>
)}
</div>
);
};
export default Sidebar;

Building a react project, if user has already subscribed then it should redirect to homepage but if user sign up then pop up planPage

I've used redux to maintain state if user has already subscribed but on reload, already subscribed users are also redirected to planPage instead of homePage because on reload state sets back to initial null state how should i do it then ?
This is my App.jsx
import { useEffect, useState } from "react";
import DetailPage from "./pages/DetailPage";
import ProfilePage from "./pages/ProfilePage";
import HomePage from "./pages/HomePage";
import LoginPage from "./pages/LoginPage";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import { selectUser, login, logout } from "./features/userSlice";
import PlanPage from "./pages/PlanPage";
import { getDocs } from "firebase/firestore";
import { auth, users } from "./firebase";
import { selectSubscription } from "./features/subscriptionSlice";
function App() {
const user = useSelector(selectUser);
const isSubscribed = useSelector(selectSubscription);
const dispatch = useDispatch(); //hook to access redux dispatch function
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((userAuth) => {
// observe the state change in user's sign in activity
if (userAuth) {
//logged in
dispatch(login({ uid: userAuth.uid, email: userAuth.email }));
} else {
//logged out
dispatch(logout());
}
});
return unsubscribe; // for cleanup the previous state
}, [dispatch]);
console.log(isSubscribed);
return (
<div>
<BrowserRouter>
{!user ? (
<LoginPage />
) : (
<>
{!isSubscribed ? (
<PlanPage />
) : (
<>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="detailScreen" element={<DetailPage />} />
<Route path="profileScreen" element={<ProfilePage />} />
<Route path="planScreen" element={<PlanPage />} />
</Routes>
</>
)}
</>
)}
</BrowserRouter>
</div>
);
}
export default App;
planPage.jsx file
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { auth } from "../firebase";
import { logout } from "../features/userSlice";
import { AiOutlineCheck } from "react-icons/ai";
import { products, users } from "../firebase";
import { getDocs, setDoc, doc } from "firebase/firestore";
import Table from "../components/Table";
//import { BiLoaderAlt } from "react-icons/bi";
import {
showSubscriptionDetail,
selectSubscription,
} from "../features/subscriptionSlice";
import { useNavigate } from "react-router-dom";
export default function PlanPage() {
const navigate = useNavigate();
const subscription = useSelector(selectSubscription);
const [prod, setProducts] = useState([]); //useState() hook, sets initial state to an empty array
const [selectedPlan, setSelectedPlan] = useState(null);
useEffect(() => {
async function unsubscribe() {
const item = await getDocs(products);
const productItem = item.docs.map((doc) => ({
id: doc.id, //id and data pushed into productItems array
...doc.data(),
}));
setProducts(productItem);
setSelectedPlan(productItem[2]);
}
unsubscribe();
}, []);
const dispatch = useDispatch();
const handleClick = () => {
auth.onAuthStateChanged(() => {
dispatch(logout());
});
};
const manageSubscription = () => {
if (subscription) navigate("/");
navigate(-1);
};
async function setData(data) {
await setDoc(doc(users, `${auth.currentUser.email}`), {
productType: data,
email: auth.currentUser.email,
subscribed: true,
activateTime: new Date().toLocaleString(),
planEndTime: `${
new Date().getMonth() + 2
}/${new Date().getDate()}/${new Date().getFullYear()}`,
});
}
const subscribeToPlan = () => {
if (!auth) return;
dispatch(showSubscriptionDetail({ subscription: true }));
setData(selectedPlan?.name);
};
return (
<div>
<header className="border-b border-white/10 bg-[#141414] ">
<img
alt=""
src="https://rb.gy/ulxxee"
width={150}
height={90}
className="cursor-pointer object-contain"
onClick={manageSubscription}
/>
<button
className="text-lg font-medium hover:underline"
onClick={handleClick}
>
Sign Out
</button>
</header>
<main className="mx-auto max-w-5xl px-5 pt-28 pb-12 transition-all md:px-10">
<h1 className="mb-3 text-lg md:text-3xl font-medium">
Choose the plan that's right for you
</h1>
<ul>
<li className="flex items-center gap-x-2 text-sm md:text-lg">
<AiOutlineCheck className=" h-5 w-5 md:h-7 md:w-7 text-[#E50914]" />{" "}
Watch all you want. Ad-free.
</li>
<li className="flex items-center gap-x-2 text-sm md:text-lg">
<AiOutlineCheck className=" h-5 w-5 md:h-7 md:w-7 text-[#E50914]" />{" "}
Recommendations just for you.
</li>
<li className="flex items-center gap-x-2 text-sm md:text-lg">
<AiOutlineCheck className=" h-5 w-5 md:h-7 md:w-7 text-[#E50914]" />{" "}
Change or cancel your plan anytime.
</li>
</ul>
<div className="mt-4 flex flex-col space-y-4">
<div className="flex w-full items-center self-end md:w-3/5">
{prod.map((product) => (
<div
className={`planBox ${
selectedPlan?.id === product.id ? "opacity-100" : "opacity-60"
}`}
key={product.id}
onClick={() => setSelectedPlan(product)} //here if i have directly called the stateSetter i.e setSelectedPlan then it is getting called soon after the component mount stage, and keeps on rerender the state and getting stuck into loops. hence ()=> setSelected()
>
{product.name}
</div>
))}
</div>
<Table products={prod} selectedPlan={selectedPlan} />
<button
disabled={!selectedPlan}
className={`mx-auto w-11/12 rounded bg-[#E50914] py-4 text-xl shadow hover:bg-[#f6121d] md:w-[420px] `}
onClick={subscribeToPlan}
>
Subscribe
</button>
</div>
</main>
</div>
);
}
subscriptionSlice.jsx (redux-reducer code)
import { createSlice } from "#reduxjs/toolkit";
export const subscriptionSlice = createSlice({
name: "subscription",
initialState: {
subscription: null,
},
reducers: {
showSubscriptionDetail: (state, action) => {
state.subscription = action.payload;
},
},
});
export const { showSubscriptionDetail } = subscriptionSlice.actions;
export const selectSubscription = (state) =>
state.subscription.subscription?.subscription;
export const subscriptionReducer = subscriptionSlice.reducer;
I implemented this features just by defining a state(issubscribed) in App.js and have access to db. if subscription exist in firebase sent back a data if not it will stay null and you can have the simple logic in your routing.

useRef is always null until I update and save my code

I'm using a useRef on a tag to get the width value. But when the page is loaded, ref is always null. The only time it gets the value, it's when I update my code and save it inside my IDE. This is the current code :
import { Navbar } from './Navbar';
import photo from './assets/photo.jpg';
import shoes from './assets/shoes.jpg';
import jump from './assets/jump.jpg';
import { getShoes } from './Request';
import { useQuery } from 'react-query';
import { motion } from 'framer-motion';
import { useEffect, useRef, useState } from 'react';
import { Loader } from './Loader';
export const Home = () => {
const { data, isSuccess } = useQuery('shoes', getShoes);
const [carouselMaxWidth, setCarouselMaxWidth] = useState<number>(0);
const ref = useRef<HTMLDivElement | null>(null);
useEffect(() => {
console.log(ref);
if (ref && ref.current) {
const tmp = ref.current?.scrollWidth - ref.current?.offsetWidth;
setCarouselMaxWidth(() => tmp);
}
}, []);
return (
<>
{isSuccess ? (
<div className='h-screen'>
<div ref={ref} className='overflow-x-hidden'>
<motion.div
className='flex min-w-fit space-x-5 cursor-grab'
drag='x'
dragConstraints={{ right: 0, left: -carouselMaxWidth }}
style={{ width: carouselMaxWidth }}
>
{data.slice(0, 10).map((i: any, key: number) => {
return (
<motion.div className='h-full w-96 pointer-events-none' key={key}>
<img src={i.img} alt={i.title} className='object-cover h-96 w-96' />
<div className='flex justify-between'>
<div>
<span>{i.title}</span>
<div className='text-opacity-50'>{i.source}</div>
</div>
<span>{i.price}</span>
</div>
</motion.div>
);
})}
</motion.div>
</div>
</div>
</div>
) : (
<Loader />
)}
</>
);
};

How to set ratings individually with localstorage?

Okay so as you can tell,I am building a recipe app where in each recipe detail page, I can rate the the recipe...but now the problem is that when I rate one recipe now, the same rating is set for all the other recipes as well. Any idea how to fix that? Apreciate any advice...thanks so much :))
Details.js
import { useParams } from 'react-router-dom';
import './Details.css';
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import Image from './vitaminDfood-1132105308-770x553.jpg';
import {Link} from 'react-router-dom'
import ReactStars from 'react-rating-stars-component'
import { RecipeContext } from './RecipeContext';
import { useContext } from 'react';
function Details() {
const [details, setDetails] = useState([]);
const { recipeId } = useParams();
const{recipes,setRecipes}=useContext(RecipeContext)
const initialRatings = JSON.parse(localStorage.getItem("rating") || "[]");
const[rating,setRating]=useState(initialRatings)
useEffect(() => {
axios
.get(`https://cookbook.ack.ee/api/v1/recipes/${recipeId}`)
.then((res) => setDetails(res.data));
});
useEffect(() => {
localStorage.setItem("rating", JSON.stringify(rating));
}, [rating])
const ratingChanged = (newRating) => {
var rate={
score:newRating
}
setRating(newRating)
axios.post(`https://cookbook.ack.ee/api/v1/recipes/${recipeId}/ratings`,rate)
.then((res) => {
console.log(res.data)
setRecipes(recipes)
})
};
return (
<>
<div className="details">
<div className="food-photo">
<img src={Image} alt="" />
<Link to="/"> <i className="fas fa-arrow-left arrow"></i></Link>
<h1>{details.name}</h1>
</div>
<div className="star-line">
{new Array(rating).fill(null).map(() => (
<i className="fas fa-star stari"/>
))}
<p className="duration"><i className="far fa-clock"></i>{details.duration}<span>min.</span></p>
</div>
<p className="info">{details.info}</p>
<h1 className="ingredience">Ingredience</h1>
<div className="ing">{details.ingredients}</div>
<h1 className="ingredience">Příprava</h1>
<p className="description">{details.description}</p>
</div>
<div className="stars">
<ReactStars
classNames="star"
size={48}
onChange={ratingChanged}
count={5}
value={1}
edit
/>
</div>
</>
);
}
export default Details;
You have a couple options:
Store a different localStorage key for each recipe
Just have one large "ratings" key in localStorage and make that an object keyed off of your recipeId.
I think the first option is the most straightforward and can be accomplished with these minor modifications:
const initialRatings = JSON.parse(localStorage.getItem(`ratings-${recipeId}`) || "[]");
useEffect(() => {
localStorage.setItem(`ratings-${recipeId}`, JSON.stringify(rating));
}, [rating, recipeId])

Resources