So here I am developing a web app. I use supabase as the database and SWR as the React Hooks. I have followed the step by step on YouTube, but then I encountered the following problem. In the index.tsx file there is an error regarding the rest parameter in the following fetcher function, even though the function is a template function provided by SWR. The error says, "Rest parameter 'args' implicitly has an 'any[]' type." .How should I fix it?
const Home: NextPage = () => {
const [myPosts, setMyPosts] = useState([])
const fetcher = (...args) => fetch(...args).then(res => res.json())
const {data, error} = useSWR('/api/get-posts', fetcher, {refreshInterval: 200})
console.log(data)
useEffect(() => {
if (!data) return
setMyPosts(data.data)
}, [data])
return (
<div className={style.wrapper}>
<Header />
<Banner />
<main className={style.main}>
<div className={style.content}>
<CreatePost />
<Feed posts={myPosts}/>
</div>
<div className={style.infoContainer}>
<About />
</div>
</main>
</div>
)
}
export default Home
I hope that my data in database can be fetched and rendered in my Next.js project.
Related
I am new to Nextjs and Stackoverflow so I will try to describe my issue as best as possible.
I have created the following folder structure in my project:
pages
api folder
index.js
sys-admin folder
createvenue.js
createuser.js
index.js
Inside sys-admin>index.js, I have a button with an onClick handler that uses next/router to push to the createvenue.js route. When I type in the URL http://localhost:3000/sys-admin/createvenue, I can see the createvenue.js page however, when I click the button in the http://localhost:3000/sys-admin page, I am directed to http://localhost:3000/createvenue which gives me a 404. My understanding was that the folder name (in this case sys-admin) would become the root and would be added to the router path to make the URL http://localhost:3000/sys-admin/createvenue.
Here is my sys-admin>index.js code:
import { useRouter } from "next/router";
export default function CreateCustomer() {
const router = useRouter();
const handleSubmit = () => {
router.push("/createvenue");
};
return (
<>
<form onSubmit={handleSubmit}>
<button className="btn btn-filled">Create New Customer</button>
</form>
</>
);
}
Here is my createvenue.js code:
import { useRouter } from "next/router";
export default function CreateVenue() {
const router = useRouter();
const handleSubmit = () => {
router.push("/createusers");
};
return (
<>
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Enter venue name" />
<button className="btn btn-filled">Next</button>
</form>
</>
);
}
I've also tried adding /sys-admin to router.push("/sys-admin/createvenue"); but it still doesn't work.
Is there a way to get next/router to add the sys-admin root to the URL?
The default Behaviour of the form is to refresh the Page once it submitted. You have to prevent the form event from refreshing the page.
export default function CreateVenue() {
const router = useRouter();
const handleSubmit = (event) => {
// preventing the form event from refreshing the page
event.preventDefault()
router.push("/createusers");
};
return (
<>
<form onSubmit={(e)=>handleSubmit(e)}>
<input type="text" placeholder="Enter venue name" />
<button className="btn btn-filled">Next</button>
</form>
</>
);
}
I've been trying to get this sveltekit code to work for the past 1 week and no matter how I tried, it doesn't seem to work.
Before I go to my code, this is the error message:
Cannot read properties of undefined (reading 'length') TypeError: Cannot read properties of undefined (reading 'length')
at Proxy.each (/Users/cadellteng/Documents/code/police/APDVolunteers/node_modules/svelte/internal/index.js:1710:31)
at eval (/src/lib/components/officer-list.svelte:74:29)
at eval (/src/lib/components/officer-list.svelte:109:5)
at officer-list.svelte:45:12
at Object.$$render (/Users/cadellteng/Documents/code/police/APDVolunteers/node_modules/svelte/internal/index.js:1745:22)
at eval (/src/lib/sections/training-record.svelte:25:94)
at Object.$$render (/Users/cadellteng/Documents/code/police/APDVolunteers/node_modules/svelte/internal/index.js:1745:22)
at eval (/src/routes/admin/index.svelte:17:97)
at Object.$$render (/Users/cadellteng/Documents/code/police/APDVolunteers/node_modules/svelte/internal/index.js:1745:22)
at Object.default (root.svelte:43:39)
This is my code to get data from Firebase:
<script lang="ts" context="module">
// Import Firebase
import { db } from '$lib/scripts/firebaseInit'; //from my custom FirebaseInit.ts
import { collection, getDocs } from 'firebase/firestore';
export async function load() {
let allOfficers = [];
console.log("Loading data");
const querySnapshot = await getDocs(collection(db, "officers"));
// console.log(querySnapshot);
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
let data = doc.data();
// console.log(doc.id, " => ", data);
allOfficers.push(data);
});
console.log(allOfficers)
return {
props: { allOfficers }
};
}
</script>
<script lang="ts">
export let allOfficers;
</script>
And the following are my code to render the data:
<div class="officer__list">
{#each allOfficers as officer}
<div class="officer__card accordion">
<div class="officer__content-details">
<h4 class="officer-name">{officer.name.displayed}</h4>
<p class="officer-rank-appointment">
Rank 1 | Head of Department
</p>
</div>
</div>
{/each}
</div>
And just in case you are wondering if my FirebaseInit.ts is not working well, I have another example that worked without changing anything in the script tags:
<div class="test-container">
<ul>
{#each allOfficers as officer}
<li>
{officer.name.displayed}
</li>
{/each}
</ul>
</div>
The above works like magic without changing anything in the script tags.
Edit: This is a component within a bigger svelte file. I realize when I make this its own route, it works...? What's wrong?
So I have managed to solve the problem and I can't use the context module to load data at the component level. After I move the script tag content for the load to the route level, it works perfectly. I just need to pass it down into individual components as props.
This is a simplified React component that uses helmet to update the link css on runtime:
function App() {
const [brand, setBrand] = useState('nike')
return (
<div className="App">
<Helmet>
<link rel="stylesheet" href={getBrandStyle(brand)} />
</Helmet>
<div>other contents here</div>
<!-- omitted the button components that change the brand state by calling setBrand -->
</div>
);
}
I have recently just used react-helmet as a declarative way to change the head tag's child and with the code I wrote above, when switching the css there is momentary lag when the page has no css stylings and then 1 second later the updated css shows up.
Even during the initial load of the page, if I use queryParameters (code above doesn't show the query parameter approach) such as
https://localhost:3000?brandQueryParam=nike
there is 1 second wherein there is no css styling before the brand css shows up.
Can you please let me know what I am missing and how to resolve this?
This is the solution that I came up with, not sure if setTimeout is the best solution so if anyone else knows a better way, please share it.
const brands = {
nike: 'nike2022',
adidas: 'adidas2017',
fila: 'fila2020'
};
function App() {
const [brand, setBrand] = useState('nike')
const [isLoading, setIsLoading] = useState(false)
const changeBrandStyleOnClick = (brand) => {
setBrand(brand)
setIsLoading(true)
}
return (
<div className="App">
<Helmet>
<link rel="stylesheet"
onChangeClientState={(newState, addedTags, removedTags) => setTimeout(() => setIsLoading(false), 1500)}
href={getBrandStyle(brand)} />
</Helmet>
{isLoading && (
<Overlay>
<Spinner/>
</Overlay>
)}
{!isLoading && (
<>
{Object.keys(brands).filter(b => b !== brand).map(b =>
(<Button onClick={() => changeBrandStyleOnClick (b)} value={b}>
<Logo
alt="default alt name"
appearance="default"
name={b}
size="small"/>
</Button>))
}
<div>other contents here</div>
</>
)}
</div>
);
}
I have a profile page that displays the user info. The page shows the user name / email and a button to create a list.
I can also edit the name and email correctly, and it reflects in the firebase instantaneously. Ok. I get the user data and I can edit it.
What I'm trying to do now is to show the lists that the user has created.
Look, this user has created one list, and what is returned to me is that he doesn't have lists.
I'll try to shorten the code as much as possible:
<script>
imports.....
import { db } from '../../firebase.config.js'
let listings = []
let auth = getAuth()
// fetch the user's listings
const fetchUserListings = async () => {
const listingsRef = collection(db, 'listings')
const q = query(
listingsRef,
where('userRef', '==', auth.currentUser.uid),
orderBy('timestamp', 'desc')
)
const querySnap = await getDocs(q)
querySnap.forEach((doc) => {
return listings.push({
id: doc.id,
data: doc.data()
})
})
}
fetchUserListings()
</script>
<!-- display the user's listings -->
<div>
{#if listings.length > 0}
<p class="listingText">My lists</p>
{#each listings as listing}
<ListingItem listing={listing.data} id={listing.id} />
{/each}
{:else}
<p class="noListings">You have no lists</p>
{/if}
</div>
My ListItem component:
<script>
export let listing
export let id
export let handleDelete
import DeleteIcon from '../../static/assets/svg/deleteIcon.svg'
</script>
<li class="categoryListing">
<a href={`/category/${listing.type}/${id}`} class="categoryListingLink">
<img src={listing.imgUrls[0]} alt={listing.name} class="categoryListingImg" />
<div class="categoryListingDetails">
<p class="categoryListingLocation">
{listing.location}
</p>
<p class="CategoryListingName">
{listing.name}
</p>
<p class="categoryListingPrice">
${listing.offer ? listing.discountedPrice : listing.regularPrice}
{listing.type === 'rent' ? '/ por mês' : ''}
</p>
<div class="categoryListingInfoDiv">
<img src="/assets/svg/bedIcon.svg" alt="cama" />
<p class="categoryListingInfoText">
{listing.bedrooms > 1 ? `${listing.bedrooms} camas` : `${listing.bedrooms} cama`}
</p>
<img src="/assets/svg/bathtubIcon.svg" alt="banheiro" />
<p class="categoryListingInfoText">
{listing.bathrooms > 1
? `${listing.bathrooms} banheiros`
: `${listing.bathrooms} banheiro`}
</p>
</div>
</div>
</a>
{#if handleDelete}
<DeleteIcon
class="removeIcon"
fill="rgb(231, 76, 60)"
onClick={() => {
handleDelete(listing.id, listing.name)
}}
/>
{/if}
</li>
Just when you think you've reached the simplest part, it's still tough.
Update:
I think that the problem is in firebase. The "docs" are empty:
Now I am in serious trouble!
querySnap.forEach((doc) => {
return listings.push({
id: doc.id,
data: doc.data()
})
})
I see two things here. The less important: The .forEach() method returns undefined, so the return is redundant. The more important: the .push() alone won't automatically trigger updates. Have a look at this section in the Docs
Did you try logging listings? I assume the data is there, it's just not displayed, so I propose to change this part to
querySnap.forEach((doc) => {
listings = [...listings, {
id: doc.id,
data: doc.data()
}]
})
or
querySnap.forEach((doc) => {
listings.push({
id: doc.id,
data: doc.data()
})
listings = listings
})
How do I troubleshoot this problem this deployment issue? I am following this tutorial. My node_modules and .next are ignored and not pushed to github. It works locally but can't seem to deploy. I have supplied both the component code as well as the page it's exported on. Let me know if you can see what I am missing.
https://www.youtube.com/watch?v=V4SVNleMitE
deployment errors
Error occurred prerendering page "/components/BlogPosts". Read more: https://nextjs.org/docs/messages/prerender-error
TypeError: Cannot read property 'fields' of undefined
at BlogPosts (/vercel/path0/.next/server/chunks/130.js:39:12)
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:673:46)
at Object.defaultGetInitialProps (/vercel/path0/node_modules/next/dist/server/render.js:315:51)
at Function.getInitialProps (/vercel/path0/.next/server/pages/_document.js:645:16)
at Object.loadGetInitialProps (/vercel/path0/node_modules/next/dist/shared/lib/utils.js:69:29)
component blog posts
export default function BlogPosts({post }) {
const {title, information,slug , thumbnail} = post.fields
return (
<div>
<div className='container w-50 h-25 mt-4'>
<Image
className='nav'
src={'https:' + thumbnail.fields.file.url}
width={thumbnail.fields.file.details.image.width}
height={thumbnail.fields.file.details.image.height}
/>
</div>
<div>
<div>
<h4 className=''>{title}</h4>
<Link href={'/contentslug/' + slug}>
<a className='btn btn-primary text-white'>Read more</a>
</Link>
</div>
</div>
</div>
)
}
Pages/Posts
import {createClient} from 'contentful'
import BlogPosts from './components/BlogPosts'
import Nav from './components/Nav'
import Logo from './components/Logo'
export async function getStaticProps() {
const client = createClient({
space: process.env.NEXT_PUBLIC_CONTENTFUL_ID,
accessToken: process.env.NEXT_PUBLIC_CONTENTFUL_TOKEN,
})
const res = await client.getEntries({content_type: 'posts'})
return {
props: {
posts: res.items ,
revalidate: 1
}
}
}
export default function Home({posts}) {
console.log(posts);
return (
<div>
<Logo/>
<Nav/>
<div className="container text-center display-5">
{posts.map(post => (
<BlogPosts key={post.sys.id} post={post}/>
))}
</div>
</div>
)
}
You have fields of undefined. this might be caused because of some strange deploying behavior if you are 100% sure your code works.
How to fix (probably):
Build your project locally. if it works, follow the next step
Comment your code in BlogPosts, inside the exported component. The code must work, so your exported component will be empty but working.
Push this code to Vercel.
Uncommit your code. (done at point 2)
Push again.
P.S. this behavior with API is sometimes caused because of API middleware you reworked.