This question already has answers here:
React setState not updating state
(11 answers)
The useState set method is not reflecting a change immediately
(15 answers)
Closed 17 days ago.
This post was edited and submitted for review 17 days ago and failed to reopen the post:
Original close reason(s) were not resolved
I make an ecommerce site I have a page that displays all the products and on each product I put a link tag that refers to my page where I will put the product details with its id like this :
<div className="flex -mx-5 overflow-x-scroll snap-x scrollbar-hide">
{products.filter(p => p.category === categoryName).map(productInfo => (
<Link href={`/product?id=${productInfo._id}`} key={productInfo._id} className="px-5 snap-start border rounded-xl shadow-md m-2 p-3 bg-white">
<Product {...productInfo}/>
</Link>
))}
</div>
and my page with product details where I get the id from the url :
import React, { useState, useEffect } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import axios from "axios";
import Layout from "../components/Layout";
const ProductPage = () => {
const router = useRouter();
const [product, setProduct] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchProduct = async () => {
try {
const response = await axios.get(`/api/products?id=${router.query.id}`);
setLoading(false);
setProduct(response.data);
console.log(product)
} catch (error) {
console.error(error);
}
};
fetchProduct();
}, [router.query.id]);
console.log(product)
return (
<Layout title={product.name}>
{loading ? (
<div className="text-center">Loading...</div>
) : (
<div className="max-w-lg mx-auto p-5">
<h1 className="text-2xl font-bold mb-5">{product.name}</h1>
<img
src={product.imageUrl}
alt={product.name}
className="w-full mb-5"
/>
<p className="mb-5">{product.description}</p>
<p className="text-xl font-bold mb-5">
Price: {product.price} {product.currency}
</p>
<Link href="/" legacyBehavior>
<a className="btn btn-primary">Go back to the products list</a>
</Link>
</div>
)}
</Layout>
);
};
export default ProductPage;
The values are not displayed so I put some console.log
When I put a console.log in the "try" product returns an empty object, and when I put the console.log after the useffect the product returns an object with my values
Related
Using Next.js I came across this problem:
As soon as I reach pages/users, I got this error
./node_modules/mongodb/lib/cmap/auth/gssapi.js:4:0
Module not found: Can't resolve 'dns'
Import trace for requested module:
./node_modules/mongodb/lib/index.js
./models/user/addedUserModel.js
./pages/api/users/index.js
./pages/users.js
https://nextjs.org/docs/messages/module-not-found
I'm trying to fetch all the users from getServerSideProps, inside pages/users
import { Layout, Meta, Card, Button } from "../components/ui";
import {useAxios} from "../utils/hooks/requests/useAxios";
import {useNotifications} from "../context/notifications/notifications_context";
const Users = ({users}) => {
const {handleDeleteRequest} = useAxios("api/users");
const {notification} = useNotifications()
const removeUser = async (_id) => {
await handleDeleteRequest(_id)
}
return (
<>
<Layout>
<Meta title="Users"/>
<Card>
{users.addedUsers.map(user => <div className="flex border-b items-center justify-between"
key={user._id}>
<div className="p-2">
<h2>{user.name}</h2>
</div>
<div>
<Button onClick={() => removeUser(user._id)} className="bg-red-500">Delete</Button>
</div>
</div>)}
</Card>
</Layout>
{notification.text ? <div
className={`${notification.isError ? 'bg-red-500 ' : 'bg-green-500 '} absolute top-0 left-0 w-full p-2 flex items-center justify-center text-white font-semibold`}>
<p>{notification.text}</p>
</div> : null}
</>
)
}
export default Users;
export async function getServerSideProps() {
const res = await fetch("http://localhost:3000/api/users")
const users = await res.json()
return {
props: { users }
}
}
The folder structure of the api endpoint is as follows -> api/users/index.js
Under the pages/users folder I've got an [id].js file to delete a single user.
So the problem lives in the getServerSideProps function or there's something else I'm missing to get rid of this dns error?
Also, I want to re-fetch all the users after removing one of them, to have a fresh users list without refreshing the page. Isn't getServerSideProps useful to do this job??
getServerSideProps allows you to fetch data on the server-side before rendering the page. This means that when a user visits the pages/users page, the data will be fetched from the server and returned as part of the props object to the component
I asked a similar question (ReactJs/JS/TailWindCSS - Infinite Image Slider), but this only solved the problem of making an infinite image slider, but I need something that is dynamic, here is the modified code from the question,
import {db} from "./../firebase";
import React, {useEffect, useState} from "react";
import { collection, onSnapshot } from "firebase/firestore";
export default function scrollImagesRight() {
const [images, setImages] = useState([]);
useEffect(()=>{
onSnapshot(collection(db, "sponsors"), (snapshot) =>{
const data: React.SetStateAction<[]> = [];
snapshot.forEach((doc) => {
data.push(doc.data());
});
setImages(data);
});
},[])
return(
<div className="w-full bg-red-100 flex justify-center">
<div className="h-[200px] m-auto overflow-hidden relative w-auto">
<ul className={"flex w-[calc(250px*",images.length,")] animate-scroll"}>
{images.map((ref, index)=>{
return(
<li key={index} className="w-[250px]">
<img src={ref.image} alt={ref.name} />
</li>
);
})}
</ul>
</div>
</div>
);
};
How can I make it so that it automatically keeps working with any number of images?
This question already has answers here:
async/await inside arrow functions (Array#map/filter)
(6 answers)
Closed 7 months ago.
I have two different API end points to fetch data from. The second endpoint requires a query parameter that comes from the first endpoints response.
I am using useSWR hook with axios inside a custom hook which looks like-
import axios from '#/lib/axios'
import { useEffect, useState } from 'react'
import useSWR from 'swr'
export const useSubjects = ({ id, path } = {}) => {
/**
* Fetcher Function for SWR
*/
const fetchSubjects = async url => {
return await axios
.get(url)
.then(res => res.data)
.catch(error => {
if (error.response.status !== 409) throw error
})
}
const fetchThumbnail = async (url, path) => {
var thumbImages = []
await path.map(async pathdata => {
// console.log(url + pathdata)
await axios
.get(url + pathdata, {
responseType: 'blob',
})
.then(res => thumbImages.push(res.data))
.catch(error => {
throw error
})
})
return thumbImages
}
/**
* swr hook for fetching subjects
*/
const {
data: subjects,
error: SubjectError,
mutate: mutateSubject,
} = useSWR(
id > 0 ? `api/get-subject-branch/${id}` : 'api/get-subject-branches',
fetchSubjects,
)
/* swr hook for fetching thumbnails */
const {
data: thumbnail,
error: thumbnailError,
mutate: mutateThumbnail,
} = useSWR(
path ? ['api/get-subject-thumbnail?thumbnail_url=', path] : null,
fetchThumbnail,
)
return {
subjects,
thumbnail,
mutateSubject,
mutateThumbnail,
}
}
Using the hook:
const AdminCourses = () => {
const [thumbnailPaths, setThumbnailPaths] = useState([])
const [thumbBlob, setThumbBlob] = useState([])
// const [thumbnail, setThumbnail] = useState()
// Subjects fetched from API using subjects hook
const { subjects, thumbnail, mutateSubject, mutateThumbnail } = useSubjects(
{
path: thumbnailPaths,
},
)
useEffect(() => {
if (subjects) {
setThumbnailPaths([])
subjects?.data?.map(sub => {
setThumbnailPaths(thumbnailPaths => [
...thumbnailPaths,
sub.thumbnail_url,
])
})
}
}, [subjects])
useEffect(() => {
if (thumbnail) {
setThumbBlob([])
thumbnail?.map(thumbnailBlobData => {
console.log(thumbnailBlobData)
var imgData = []
imgData.push(thumbnailBlobData)
setThumbBlob(thumbBlob => [
...thumbBlob,
URL.createObjectURL(new Blob(imgData, { type: 'Image/*' })),
])
})
}
// console.log('calling effect to update data')
return () => {
thumbBlob.map(data => URL.revokeObjectURL(data))
}
}, [subjects, thumbnail])
useEffect(() => {
if (thumbnailPaths?.length != 0) {
console.log("calling thumbnail swr", thumbnailPaths, thumbBlob);
mutateThumbnail()
}
}, [thumbnailPaths])
return (<>
{subjects?.data?.map((data, ind) => (
<div key={'subject' + ind}>
<hr className="bg-black h-101 mt-8" />
{/* Start of Subjects / Courses */}
<div className="flex justify-between items-center lg:flex-col md:flex-row sm:flex-col my-4">
{/* subject thumbnail */}
<div className="w-32 pl-8 lg:mt-8 md:mt-0 sm:mt-8">
<Image
src={
thumbBlob?.length != 0
? thumbBlob[ind]
: '/image/blog-2.jpg'
}
width={5706}
height={3840}
layout="responsive"
objectFit="cover"
className="rounded-xl"
/>
</div>
<div className="flex flex-col lg:mt-8 md:mt-0 sm:mt-8">
{/* subject title */}
{/* <h1 className="text-xl font-bold">
{data.subject_name}
</h1> */}
<CourseNav
DataName={data.subject_name}
className="relative -left-6 bg-btn-color font-bold rounded-md shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
/>
</div>
{/* subject description */}
<h4 className="text-sm font-bold self-center w-2/5 lg:text-base lg:w-3/4 lg:mt-8 md:mt-0 md:w-2/5 md:text-sm sm:text-base sm:w-3/4 sm:mt-8">
{data.subject_description}
</h4>
<div className="flex justify-between items-center lg:my-8 md:my-0 sm:my-8">
{/* Edit Button */}
<div className="cursor-pointer hover:scale-110 transform transition-all duration-500 pr-8">
<FontAwesomeIcon icon={faPen} size="2x" />
</div>
{/* delete video btn */}
<div className="cursor-pointer hover:scale-110 transform transition-all duration-500">
<DeleteIcon />
</div>
</div>
</div>
{/* End of Subjects / Courses */}
</div>
))}
</>
)}
The given custom hook in the first snippet is used in the component given in the second snippet.
The Data:
There are two types of data fetched using these end points
First, an array of json objects containing subjects
Second, array of blob of images - thumbnail
The subjects array contains thumbnail_url in each element.
The images are fetched using those thumbnail_url
The Problem:
The thumbnail_url paths are stored in the state hook thumbnailPaths which is an array of all thumbnail_urls
Again, thumbnailPaths is passed as an object parameter in useSubjects hook.
Initial call to useSubjects hook is definitely an empty array of thumbnailPaths. But later when the thumbnailPaths array is populated with data it is expected to call the useSubjects hook with the updated thumbnailPaths array.
Shouldn't it re-render the component and call useSubjects again so that the second useSWR in useSubject custom hook gets called again?
What is happening in this case is. Despite thumbnailPaths being populated the second useSWR hook is not being called unless mutateSubject() is being called which I am calling using a button click.
The call to mutateThumbnail seems to have no effect whatsoever.
I am getting thumbnail images only on calling mutateSubjects
What I want is to get "All" data in one go without having to reload or press any button.
You can't await for an array like that:
await path.map(async pathdata => {
// console.log(url + pathdata)
await axios
.get(url + pathdata, {
responseType: 'blob',
})
.then(res => thumbImages.push(res.data))
.catch(error => {
throw error
})
})
Array is not a Promise-like so all this stuff inside fetchThumbnail resolves immediately with an empty array. You need to you use Promise.all if you want to wait for an array of promises.
I am sick of defining states to render Components on condition. Most of the time I just need to show some kind of notification or alert. I want to know how can I render a component by calling a function from that Component.
I have found some sample code that is doing exactly what I want, but I can´t reverse engineer it to implement this on my own as I have no clue how the Modal.info() function is adding itself to the DOM.
I want to recreate the Modal Component for myself and display it by calling MyModal.info().
import { Modal, Button, Space } from 'antd';
const Item = (props: ItemProps) => {
const { itemGroup, items } = props;
function info() {
Modal.info({
title: 'This is a notification message',
content: (
<div>
<p>some messages...some messages...</p>
<p>some messages...some messages...</p>
</div>
),
onOk() {},
});
}
return (
<div className="py-6">
<div
onClick={() => info()}
className="cursor-pointer py-6 px-6 text-3xl font-heading font-bold bg-primary text-white"
>
<p>{itemGroup.text}</p>
</div>
<div className={`${isOpen ? 'block' : 'hidden'} duration-200 transition-all p-3 bg-gray-200`}>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-6">
{items.map((x) => (
<ItemCard key={x.id} itemData={x} />
))}
</ul>
</div>
</div>
);
};
I came up with following solution.
Notification.tsx
import React from 'react';
import ReactDOM from 'react-dom';
export class Notifier {
static warn() {
if (!document) return;
const rootElement = document.getElementById('__next');
ReactDOM.render(React.createElement(Notification), rootElement);
}
}
const Notification = () => {
return (
<div className="w-full h-full bg-red-500 absolute top-0 left-0">Notification Content</div>
);
};
With this solution I can insert anywhere I want my Modal by calling Notifier.warn().
The only thing I am insecure about is the bundle size from ReactDOM which actually ads 125Kb to Notification.tsx.
You should be able to call Modal.info() like this
<Modal.info
title='This is a notification message',
content={) => (
<div>
<p>some messages...some messages...</p>
<p>some messages...some messages...</p>
</div>
)}
onOk={() => {}}
/>;
All functional components can be called like components.
If this doesn't work then Modal.info is not a component.
To trigger it you should follow the first example from the docs.
https://ant.design/components/modal/
You need to manage some sort of state to tell the Modal to open, functional components can manage something like state using hooks.
Like here
https://reactjs.org/docs/hooks-state.html
For custom, you will need to create your own modal design, likely in a react portal, design as you want. But opening/closing will be handled through useState hooks.
import React, { useState } from 'react';
const Component = props => {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>Open</Button>
{open && <Modal>
<Button onClick={() => setOpen(false)}>Close</Button>
</Modal> }
</>
)
}
I'm trying to render a blog as a card then open it up as a page , but its proving to be difficult using Gatsby. I did the same thing fine with react using React router and useLocation but it doesn't seem to be working with Gatsby.
I switched to reach router as suggested in another post but that doesnt work. Im looking for another method now that perhaps does not need to use useLocation.
I kept getting this error when I used react-router-dom:
Invariant failed: You should not use <Link> outside a <Router>
function Blog() {
const [blogs, setBlogs] = useState([])
const [image, setImage] = useState()
const [selectedBlog, setSelectedBlog] = useState(blogs)
useEffect(() => {
fetch("http://cdn.contentful.com...")
.then(response => response.json())
.then(data =>
setBlogs(data.items)
)
}, [])
console.log(blogs)
return (
<>
<div className="card-flex" >
{selectedBlog !== null ? blogs.map((blog =>
<Card title={blog.fields.title} date={blog.fields.date} introduction={blog.fields.introduction} mainBody1={blog.fields.mainBody1} mainBody2={blog.fields.mainBody2} setSelectedBlog={selectedBlog}
/>
)):
<Article title={blogs.find(d => d.fields.title === selectedBlog)} />
}
</div>
</>
)
}
export default Blog
Blog Card
function Card(props) {
console.log(props)
return (
<div class="container">
<div class="card">
<div class="card-header">
<img style={{backgroundImage: "url('https://i.pinimg.com/564x/7f/bb/97/7fbb9793b574c32f5d28cae0ea5c557f.jpg')"}}/>
</div>
<div class="card-body">
<span class="tag tag-teal">{props.tags}</span>
<h4>{props.title}</h4>
<p style={{fontSize:"17px", paddingTop:"10px"}} >{props.introduction}</p>
<div class="card-user">
<Link
to={{
pathname: '/article',
state: {
title: props.title,
introduction: props.introduction
}
}}
>
<button>read more</button>
</Link>
<div class="user-info">
<h5 >{ props.date}</h5>
</div>
</div>
</div>
</div>
</div>
)
}
export default Card
Article Page
import React from 'react'
import './Article.css'
import { useLocation } from "#reach/router"
function Article(props) {
// useLocation to access the route state from Blog.js
const { state = {} } = useLocation();
console.log(state)
return (
<div className="main">
<h1 className="title">{state.title}</h1>
<p className="intro">{state.introduction}</p>
<p className="main1">{state.mainBody1}</p>
<p className="main2">{state.mainBody2}</p>
</div>
)
}
export default Article
I believe you're not supposed to use react-router on a Gatsby project: https://www.gatsbyjs.com/docs/reference/routing/creating-routes/
For a normal project you could do:
Go to your top-most element and wrap it with a Router. https://reactrouter.com/web/api/BrowserRouter
You basically have to search for ReactDom.render(<YourApp />) and do ReactDom.render(<Router><YourApp /></Router>)