Which is the best way to render dynamic items in NextJS? - next.js

I´m am new to NextJS and i am finding difficult to understand the difference between getSaticProps and getServerSideProps.
I have a page with a form to add a new event and store it in a json file:
import React from 'react'
import Layout from '#/components/Layout'
import styles from '#/styles/AddEvent.module.css'
export default function AddEventPage() {
const submitHanlder = (e) => {
e.preventDefault();
const formData = {
title: e.target.title.value,
description: e.target.description.value
}
fetch('/api/events', {
method: 'POST',
body: JSON.stringify(formData)
});
console.log(formData)
}
return (
<Layout title='Add New Event'>
<h1>Add Event</h1>
<div className={styles.container}>
<form className={styles.form} action="" onSubmit={submitHanlder}>
<label className={styles.label} >Title</label>
<input type="text" name="title" />
<label className={styles.label} >Description</label>
<input type="text" name="description"/>
<label className={styles.label}htmlFor="">Date</label>
<input type="date" />
<button type='submit' >Submit</button>
</form>
</div>
</Layout>
)
}
This is my api:
const handler = async (req , res) => {
if(req.method === 'POST'){
await fetch('http://localhost:3001/events', {
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
body: req.body
})
return res.status(201).json({ message: 'evento agregado' });
}
return res.status(400).json({ error: 'no se pudo agregar el evento' });
}
export default handler;
And i am storing my data in my db.json:
{
"events": [
{
"id": 1,
"title": "Recital coldplay",
"description": "Recital de coldplay en River"
},
{
"title": "Recital metalica",
"description": "Recital de metalica en velez",
"id": 2
}
]
}
Until now i am rendering my events as i used to do it in react.js by importing the {events} and doing a map function, and it works:
import React from 'react'
import Layout from '#/components/Layout'
import { events } from '../../db.json'
export default function EventsPage({ }) {
return (
<Layout>
<h1>My Events</h1>
<div>
{events.map((event) => {
return (
<div>
<h1>{event.title}</h1>
<p>{event.description}</p>
</div>)
})}
</div>
</Layout>
)
}
This works perfectly fine but i know nextjs provides different methods to make this easier? Which method should i use to optimize my code? Can anyone give an example? Is there any step of my code i could avoid?

getServerSideProps is essentially SSR for the page which executes per request.
getStaticProps is static site generation at build time (plus on a timed interval).
You could use either mechanism to read from your json file server side and pass the content in as props.

Related

How can i export object to render a list of items in NextJs?

I have a form in a page and send that data to my API and store it a json file inside an object i want to export that object to render with a map function all my elements.
add.js:
import React from 'react'
import Layout from '#/components/Layout'
import styles from '#/styles/AddEvent.module.css'
export default function AddEventPage() {
const submitHanlder = (e) => {
e.preventDefault();
const formData = {
title: e.target.title.value,
description: e.target.description.value
}
fetch('/api/events', {
method: 'POST',
body: JSON.stringify(formData)
});
console.log(formData)
}
return (
<Layout title='Add New Event'>
<h1>Add Event</h1>
<div className={styles.container}>
<form className={styles.form} action="" onSubmit={submitHanlder}>
<label className={styles.label} >Title</label>
<input type="text" name="title" />
<label className={styles.label} >Description</label>
<input type="text" name="description"/>
<label className={styles.label}htmlFor="">Date</label>
<input type="date" />
<button type='submit' >Submit</button>
</form>
</div>
</Layout>
)
}
events.js:
const handler = async (req , res) => {
if(req.method === 'POST'){
await fetch('http://localhost:3001/events', {
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
body: req.body
})
return res.status(201).json({ message: 'evento agregado' });
}
return res.status(400).json({ error: 'no se pudo agregar el evento' });
}
export default handler;
This is my db.json: where i store the events i add with my form.
{
"events": [
{
"id": 1,
"title": "Recital coldplay",
"description": "Recital de coldplay en River"
},
{
"title": "Recital metalica",
"description": "Recital de metalica en velez",
"id": 2
}
]
}
How can i export this object above to render all my events in my front end?
This is what i tried:
index.js:
import React from 'react'
import Layout from '#/components/Layout'
import events from '../../db.json'
export default function EventsPage() {
return (
<Layout>
<h1>My Events</h1>
<div>
{events.map((event) => {
return <h1>{event.title}</h1>
})}
</div>
</Layout>
)
}
But i get this error: TypeError: _db_json__WEBPACK_IMPORTED_MODULE_3__.map is not a function
Instead of:
import events from '../../db.json'
Should be:
import { events } from '../../db.json'

Nextjs SendGrid Mailing Warning: List API resolved

I am trying to setup a Sendgrid Newletter signup, using the following code, stored in pages/api
import axios from "axios";
export default async function handler(req, res) {
if (req.method === "PUT") {
axios
.put(
"https://api.sendgrid.com/v3/marketing/contacts",
{
contacts: [{ email: `${req.body.mail}` }],
list_ids: [process.env.SENDGRID_MAILING_ID],
},
{
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
},
}
)
.then((result) => {
// return
res.status(200).send({
message:
"Your email has been succesfully added to the mailing list. Welcome 👋",
});
})
.catch((err) => {
// return
res.status(500).send({
message:
"Oups, there was a problem with your subscription, please try again or contact us",
});
});
}
}
The front end component looks similar to
import axios from "axios";
import { toast } from "react-toastify";
import { useState } from "react";
const MailingList = () => {
const [mail, setMail] = useState(null);
const [loading, setLoading] = useState(false);
const subscribe = () => {
setLoading(true);
axios
.put("api/mailingList", {
mail,
})
.then((result) => {
if (result.status === 200) {
toast.success(result.data.message);
setLoading(false);
}
})
.catch((err) => {
console.log(err);
setLoading(false);
});
};
return (
<div className='my-10'>
<hr className='my-5' />
<h2 className='text-3xl md:text-3xl font-semibold font-title'>
Stay Tuned!
</h2>
<label className='label'>
<p className='text-lg max-w-xl text-center m-auto leading-9'>
Want to be the first to know when SupaNexTail launches and get an
exclusive discount? Sign up for the newsletter!
</p>
</label>
<div className='mt-5'>
<input
onChange={(e) => {
setMail(e.target.value);
}}
type='email'
placeholder='Your email'
className='input input-primary input-bordered'></input>
<button
onClick={subscribe}
className={`btn ml-3 ${
loading ? "btn-disabled loading" : "btn-primary"
}`}>
I'm in!
</button>
</div>
<hr className='my-5' />
</div>
);
};
export default MailingList;
The emails are actually being added to the Sendgrid mailing list, but no response error is being displayed, email field is not cleared. And this is displayed in the console:
API resolved without sending a response for /api/MailingList, this may result in stalled requests.
The same console warning is displayed when return res.status(..
Need some advice on how to solve this!

Next.js secure authenticaton

I am trying to make a secure authentication with next.js
I've followed this article because it shows how do to it with api route and proxy server, but I do not understand why he uses this http-proxy module
I did the same thing without it and it seems to work perfectly fine.
Here is my pages/login.js file
import { Button } from '#mui/material'
import { useState } from 'react'
export default function loginPage() {
const [message, setMessage] = useState();
const handleSubmit = async (event) => {
event.preventDefault();
const data = {
username: event.target.username.value,
password: event.target.password.value
};
const response = await fetch('/api/login', {
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify(data),
});
setMessage(response.message);
}
return (
<div className="block">
<form onSubmit={handleSubmit}>
{message ? <p className="message">{message}</p> : ""}
<label htmlFor="first">Username</label>
<input type="text" id="username" name="username" variant="outlined" required />
<label htmlFor="last">Password</label>
<input type="text" id="password" name="password" variant="outlined" required />
<div>
<Button className="btn" type="submit" variant="outlined">Submit</Button>
</div>
</form>
</div>
)
}
Here is my pages/api/[...catchAll].js
import Cookies from "cookies"
export default async function handler(req, res) {
console.log("HERE");
const response = await fetch("http://localhost:7000/register", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(req.body)
}).then((res) => res.json());
const cookies = new Cookies(req, res);
cookies.set("authorization", response.token, {
httpOnly: true,
sameSite: "lax",
maxAge: 100000
});
res.status(200).json(response)
}
My front-end pages/login.js sends a request to pages/api[...catchAll].js and then a request to my back-end is being made. My back-end returns
{
message: "success",
token: crypto.randomBytes(48).toString('hex')
}
and then pages/api/[...catchAll].js sets my cookies. Why in the article the person uses the httpProxy module ? Is it more secure ? Why ?
I've seen this technique in a lot of places because of secure measurements, but I do not understand why they use this proxy server.
Could someone please explain ?

How to populate FormKit input fields with dynamic data fetched from a database

I'm making a fullstack app with vue3, axios using FormKit. For editing existing records I want to populate the input fields with the current data fetched from a mysql database. I stripped down the code to everything needed to display my problem, which in this code example is populating the FormKit input field with the lotnumber I fetched via the asynchronous function "getLotById". The lotnumber appears in the paragraph section but not in the input field. How can I properly delay the rendering of the FormKit element until the lotnumber has been fetched? Here's my code:
<script>
// import axios
import axios from "axios";
export default {
name: "LotEdit",
data() {
return {
lotnumber: this.lotnumber
}
},
props: {
lotid: Number
},
created: async function () {
await this.getLotById();
},
methods: {
// Get Lot By Id
async getLotById() {
try {
const response = await axios.get(`http://localhost:5000/lot/${this.$route.params.id}`);
this.lotnumber = response.data.lotnumber;
console.log(response.data);
}
catch (err) {
console.log(err);
}
},
}
};
</script>
<template>
<div>
<FormKit
type="text"
name="lotnumber"
label="lotnumber"
placeholder=""
validation="required"
:value="lotnumber"
/>
</div>
<div>
<p> Here the lotnumber appears: {{ lotnumber }}</p>
</div>
</template>
I suggest using a v-model on the FormKit input. Because it is two-way bound it means as soon as the async/await completes the data is populated on the template too. Something like...
<FormKit
v-model="lotnumber"
type="text"
name="lotnumber"
label="lotnumber"
placeholder=""
validation="required"
:value="lotnumber"
/>
Getting a little smarter I managed to solve the problem in the following way:
<script>
// import axios
import axios from "axios";
export default {
name: "LotEdit",
data() {
return {
lotnumber: this.lotnumber
}
},
props: {
lotid: Number
},
mounted: async function () {
const response = await this.getLotById();
const node = this.$formkit.get('lotnumber')
node.input(response.data.lotnumber, false)
},
methods: {
// Get Lot By Id
async getLotById() {
try {
const response = await axios.get(`http://localhost:5000/lot/${this.$route.params.id}`);
console.log(response.data);
return response;
}
catch (err) {
console.log(err);
}
},
}
};
</script>
<template>
<div>
<FormKit
type="text"
id="lotnumber"
name="lotnumber"
label="lotnumber"
placeholder=""
validation="required"
:value="lotnumber"
/>{{ lotnumber }}
</div>
</template>
Feel free to post any recommendations as I'm not a pro yet...
I'm also still figuring out how to handle controlled forms but I guess an alternative way to do it is with Form Generation
<script>
export default {
// ...
async setup() {
try {
const response = await axios.get(`http://localhost:5000/lot/${this.$route.params.id}`);
const schema = [
{
$formkit: "text",
label: "Lot Number",
value: response.data.lotnumber,
validation: "required",
},
];
} catch (err) {
console.log(err);
}
return { schema }
}
// ...
}
</script>
<template>
<FormKit type="form">
<FormKitSchema :schema="schema" />
</FormKit>
</template>

redux-form/immutable, how to update state in reducer.plugin?

currently I'm trying to use redux-form/immutable in ma brand new project and I'm facing so problems which I can't resolve (aspecially, with this cool reducer.plugin), probably couse I'm kinda new to immutable and stuff like that.
Appreciating and advice or help on topic.
so, first of all:
import { combineReducers } from 'redux-immutable';
import routing from './routeReducer';
import { reducer as formReducer } from 'redux-form/immutable';
const rootReducer = combineReducers({
routing,
form: formReducer.plugin({
signupForm: (state, action) => {
switch(action.type) {
case 'SIGNUP_FACEBOOK':
return {
console.log(state);
}
default:
return state
}
}
})
});
while non of the forms field and touched this console.log(state) returns undefined, so here is the question: how may I initialize the form with some kind of state and actually update in on some sort of action ('SIGNUP_FACEBOOK' for this example)?
here is the form code sample:
import React, { PropTypes } from 'react';
import { Field, reduxForm } from 'redux-form/immutable';
import renderInputControl from '../../../common/InputControl';
import validate from './validate';
const SignUpForm = (props) => {
const { handleSubmit, submitting, invalid } = props;
return (
<form onSubmit={handleSubmit} className='signup__form'>
<Field name='name' type='text' component={renderInputControl} label='Full Name'/>
<Field name='email' type='email' component={renderInputControl} label='Email'/>
<Field name='password' type='password' component={renderInputControl} label='Password'/>
<Field name='tel' type='tel' component={renderInputControl} label='Phone Number'/>
<button type='submit' disabled={submitting || invalid}>Sign Up</button>
</form>
);
};
SignUpForm.propTypes = {
handleSubmit: PropTypes.func.isRequired,
onSubmit: PropTypes.func,
submitting: PropTypes.bool,
invalid: PropTypes.bool
};
export default reduxForm({
form: 'signupForm',
validate
})(SignUpForm);
and the second question is: even after I touch any of the fields (so it actually get some state, e.g. console.log(state) returns Map...), how should I update it, couse code:
state.getIn(['fields', 'name']).set('visited', false).set('touched', false)
which should set 'touched' and 'visited' to false doesn't do anything?
Best of luck!

Resources