Redirect React Component when submit form express - node.js

I'm trying to redirect to a React Component when submit the form with express. This is my code:
Express.js
app.use(bodyParser.urlencoded({extended: true}))
app.use(express.static(publicPath));
app.get('*', (req, res) => {
res.sendFile(path.join(publicPath, 'index.html'))
})
app.post('/charge', (req, res) => {
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY)
stripe.charges.create({
amount: req.body.totalAmount * 100,
currency: 'eur',
source: req.body.stripeToken,
description: "Example charge",
receipt_email: req.body.receiptEmail
}).then((charge) => {
console.log(req.body)
}).catch((err) => {
console.log(err)
})
CheckoutForm.js
handleSubmit = (e) => {
e.preventDefault()
this.props.stripe.createToken({ name: this.state.name }).then(({ token }) => {
console.log(token)
this.stripeTokenHandler(token)
})
}
stripeTokenHandler = (token) => {
const form = document.getElementById('form')
const hiddenInput = document.createElement('input')
hiddenInput.setAttribute('type', 'hidden')
hiddenInput.setAttribute('name', 'stripeToken')
hiddenInput.setAttribute('value', token.id)
form.appendChild(hiddenInput)
form.submit()
}
render() {
return (
<div className="payment-container">
<form id="form" className="checkout-form" onSubmit={this.onSubmit}>
<label>
Card Number
<CardNumberElement
onChange={this.onChange}
style={{ base: { color: 'white' } }}
/>
</label>
<label>
Titular Name
<input
type="text"
value={this.state.name}
onChange={this.handleText}
className="input-text"
placeholder="Full Name"
/>
</label>
<label>
Expiration Date
<CardExpiryElement style={{base: {color: 'white'}}} />
</label>
<label>
CVC
<CardCVCElement style={{base: {color: 'white'}}} />
</label>
<label>
Email
<input
type="text"
value={this.state.email}
onChange={this.handleEmail}
className="input-text"
name="receiptEmail"
placeholder="Email"
/>
</label>
<button className="btn-buy">Pay now</button>
</form>
</div>
)}
}
The form submitted correctly and register the payment. When I submitted now gets the request body. What's the way to redirect a component with the request form?

If what you're looking for is your component to redirect once you hit the "Pay Now" button then I suggest you change your <button> to a <Link> component using React Router.
It would look like this
<Link to='/charge' className='btn-buy' onClick={this.formSubmit}>Pay Now</Link>
Then just remove the this.formSubmit from the form itself since its now on the button. It will redirect and submit.
If you want to WAIT to redirect after you get a reply, then in your formSubmit function, after you get a reply back from the server you can use this.props.history.push('/charge') in then then portion of your form submitter.
Keep in mind you will need to import { Link } from 'react-router-dom' for this.
Hope it helps!

Another way you could handle this is by using a Redirect component from React Router. Something like this:
// in the route, if the post was successful
res.json({data: 'whatever data you want', success: true})
// and if it was unsuccessful
res.json({success: false})
And here's a really stripped down react component to go with it:
class FormComponent extends Component {
constructor() {
super();
this.state = {
// inputs or whatever
success: true,
}
this.handleFormSubmit = this.handleFormSubmit.bind(this)
}
handleFormSubmit(e) {
e.preventDefault();
fetch('send the data', { with: 'some options' })
.then(res => res.json())
.then(jsonRes => {
const { success } = jsonRes;
this.setState({ success });
})
.catch(err => console.log(err))
}
// assorted other stuff to handle inputs or whatever
render() {
return (
<div>
<form onSubmit={this.handleFormSubmit}>
{/* inputs or whatever */}
</form>
{this.state.success && <Redirect push to='/some-other-url' />}
</div>
)
}
}
If this.state.success evaluates to true, the redirect will render; otherwise, it won't at all.
This allows you to make sure the request was successful before redirecting to another page. It also lets you give the user an error message, do something with whatever the response is, etc etc. Maybe you'd need to move the state and so on further up your component tree so more components have access to the response data, if that's something they need.

Related

how to post form data to the server backend

Form.js
import "./form.css";
import React, {useEffect,useState} from "react";
import {addBeauty,deleteB} from "./beauty";
import Modal from "./Modal";
import axios from 'axios';
export default function CreateForm() {
const [Name, setName] = useState("");
const [Intro, setIntro] = useState("");
const [isOpen, setIsOpen] = useState();
const [backendData, setBackendData] = useState([{}]);
useEffect(()=>{
fetch("http://localhost:5000/api").then(
response=>response.json()
).then(
data=>{ setBackendData(data)}
)
},[]);
const handleSubmit = (event)=>{
event.preventDefault();
axios.post("http://localhost:5000/api",{
id: userList[userList.length - 1].id + 1, Name:Name, Introduction:Intro
}).then(res=>{
console.log(res.data);
})
}
return (
<div className="container">
<form className="add" onSubmit={handleSubmit} >
<h2>User</h2>
<label htmlFor= "name">Name</label>
<input type="text" value={Name}
onChange={(event) => {setName(event.target.value);}}/>
<label htmlFor= "Intro">Introduction</label>
<input type="text" value={Intro}
onChange={(event) => {setIntro(event.target.value);}}/>
<p></p>
<p></p>
<div className = "press">
<button id = "1" type="submit">
Add Beauty
</button>
<button id = "2"
onClick={clearForm}
>
Clear Form
</button>
</div>
</form>
<br></br>
<br></br>
<br></br>
<div className="display">
{(typeof userData.user1 === 'undefined')?(
<h1>Loading</h1>):(
backendData.user1.map((user,i)=>{
return (
<div>
<h1> {user.Name}</h1>
<button onClick={()=>{
setIsOpen(user.id);
}}>View in popup</button>
<Modal open={isOpen === user.id} onClose={()=>setIsOpen(undefined)}>
<h3> {User.Introduction}</h3>
</Modal>
</div>
);})
)}
</div>
</div>
);
}
Server.js
const express = require('express');
const app = express();
const cors=require("cors");
const corsOptions ={
origin:'*',
credentials:true, //access-control-allow-credentials:true
optionSuccessStatus:200,
}
app.use(cors(corsOptions)) // Use this after the variable declaration
app.get("/api",(req,res)=> {
res.json({"user1":[
{
id: 1,
Name: "Isabella",
},
{
id:2,
Name: "Catalina
}
]})
});
app.listen(5000,()=>{
console.log("Server started on port 5000");
})
I create a from using react. And I try to send the formdata to backend and insert the formdata into the data stored at backend using axios.post. But it doesn't work. I know it's because I didn't add the prefix of backend data "user1" in axios.post. But I am not sure how to do that. Could anyone help me with this?
You have not created the route on the server correctly. You have opened a route for GETting "/api" but you need to open a route for POSTing
Replace this line:
app.get("/api",(req,res)=> {
with
app.post("/api",(req,res)=> {
Hi Here you need to create one route for post API as below
app.post("/api",(req,res)=> {
console.log(req.body) //here you got the requested data.
res.send("Success !");
});

How i can update the image url if user select new file other wise image url not update in nodejs

I'm creating a book project where i'm saving the books images into the cloudinary and there url's saving into the mongodb database which are working well.But i'm facing issue during the updation of a book when i update my book then the url of book is not updated and console giving me error Cannot read properties of undefined (reading 'map') where i want to update the url with new one url of image but its not working Please any one can solve this
this is my update.js code
module.exports.updateBook = async (req, res) => {
try {
const { id } = req.params;
const book = req.body;
const singleBook = await Book.findById(id);
// Delete Prvious Url From the Cloudinary and Reset It to the new ..
cloudinary.v2.uploader.destroy(singleBook.image[0].filename);
book.image = req.files.map((f) => ({
url: f.path,
filename: f.filename,
}));
console.log("Single Book ===", singleBook);
const updateBook = await Book.findByIdAndUpdate(
id,
{ $set: book },
{ new: true }
);
if (updateBook) {
res
.status(200)
.json({ success: true, message: "Book Updated Successfully!" });
} else {
res.status(400).json({
success: false,
message: "Book Not Updated There Is an error!",
});
}
} catch (err) {
console.log("** Error In Update Book **", err.message);
}
};
this is my route handler
const express = require("express");
const router = express.Router();
const book = require("../controller/book");
const authenticated = require("../middleware/verifyToken");
const multer = require("multer");
const { storage } = require("../cloudinary");
const upload = multer({ storage });
// Update Book By ID
router.route("/:id").put(authenticated, upload.array("image"), book.updateBook);
module.exports = router;
this is my reactjs update method
const formik = useFormik({
initialValues: {
title: book?.title,
author: book?.author,
price: book?.price,
description: book?.description,
image: book?.image[0].url,
},
validationSchema: validationSchema,
enableReinitialize: true,
onSubmit: (values) => {
const formData = new FormData();
formData.append("title", values.title);
formData.append("price", values.price);
formData.append("description", values.description);
formData.append("author", values.author);
formData.append("image", values.image);
Axios.put(`${Base_URL}/book/${id}`, values, {
headers: {
Authorization: authHeader(),
},
})
.then((res) => {
if (res.data.success) {
message = res.data.message;
setAlertContentupdate(message);
setAlertupdate(true);
setTimeout(() => {
handleClose();
navigate(`/book/${id}`);
getBook();
console.log("Response == ", res.data.message);
}, 3000);
}
})
.catch((err) => {
console.log("Error ====", err.message);
});
},
this is my jsx code for updating book
<form onSubmit={formik.handleSubmit}>
<TextField
name="title"
autoFocus
margin="dense"
label="Book Title"
type="text"
fullWidth
variant="standard"
value={formik.values.title}
onChange={formik.handleChange}
error={formik.touched.title && Boolean(formik.errors.title)}
helperText={formik.touched.title && formik.errors.title}
/>
<TextField
name="author"
margin="dense"
label="Book Author"
type="text"
fullWidth
variant="standard"
value={formik.values.author}
onChange={formik.handleChange}
error={formik.touched.author && Boolean(formik.errors.title)}
helperText={formik.touched.author && formik.errors.author}
/>
{/* File Input Field */}
{/* Picture Input */}
<input
type="file"
name="image"
accept=".png, .jpeg, .jpg"
onChange={(e) => {
formik.setFieldValue("image", e.target.files[0]);
}}
/>
{formik.touched.image && formik.errors.image ? (
<div style={{ color: "#e53935", fontSize: "12px" }}>
{formik.errors.image}
</div>
) : null}
{/* Price Input Field */}
<TextField
name="price"
margin="dense"
label="Book Price"
type="text"
fullWidth
variant="standard"
value={formik.values.price}
onChange={formik.handleChange}
error={formik.touched.price && Boolean(formik.errors.price)}
helperText={formik.touched.price && formik.errors.price}
/>
<TextField
name="description"
margin="dense"
label="Book Description"
type="text"
fullWidth
variant="standard"
value={formik.values.description}
onChange={formik.handleChange}
error={
formik.touched.description &&
Boolean(formik.errors.description)
}
helperText={
formik.touched.description && formik.errors.description
}
/>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button type="submit">Update</Button>
</DialogActions>
</form>
In formik i'm getting the book data from back end api's and putting into the formik initial values But Problem is that when i clicked on the update button then the backend compiler giving me this error Cannot read properties of undefined (reading 'map') Please any one can solve this thanks in advance
So this line looks like the issue for me:
cloudinary.v2.uploader.destroy(singleBook.image[0].filename);
Using this is actually deleting your asset so you are probably want to just update it using the explicit API. See https://cloudinary.com/documentation/image_upload_api_reference#explicit
So maybe something like:
cloudinary.v2.uploader.explicit(singleBook.image[0].filename);
Let me know if this helps?

How to pass data from React form to Node code?

I was building a weather app using OpenWeather API. The API was fetched in Node, then data was passed to React front end, code as follows:
Node index.js
const express = require('express');
const cors = require('cors');
const app = express();
const axios = require('axios');
const dotenv = require('dotenv');
dotenv.config();
const url = `http://api.openweathermap.org/data/2.5/weather?q=london,uk&APPID=${process.env.REACT_APP_WEATHER_API_KEY}`;
app.use(cors());
app.get('/', (req, res) => {
res.send('go to /weather to see weather')
});
app.get('/weather', (req, res) => {
axios.get(url)
.then(response => {res.json(response.data)})
.catch(error => {
console.log(error);
});
})
let port = process.env.PORT || 4000;
app.listen(port, () => {
console.log(`App running on port ${port} `);
});
The weather data can then be viewed in http://localhost:4000/weather. Then React is used to display the data. Assume there is a simple React component to accept weather input and update state:
React WeatherForm.js
import React from 'react';
class WeatherForm extends React.Component {
constructor(props) {
super(props);
this.state = {
country: '',
city: ''
}
}
updateLocation(e) {
this.setState({
country: e.target.value,
city: e.target.value
});
}
render() {
return (
<form>
<div className="field">
<label className="label">Country</label>
<div className="control">
<input
className="input"
type="text"
placeholder="Type country name here"
onChange={e => this.updateLocation(e)} />
</div>
</div>
<div className="field">
<label className="label">City</label>
<div className="control">
<input
className="input"
type="text"
placeholder="Type city name here"
onChange={e => this.updateLocation(e)} />
</div>
</div>
<div className="field">
<div className="control">
<input
type='submit'
value='Search' />
</div>
</div>
</form>
)
}
}
export default WeatherForm
Question: How can I pass the country and city user input from the React app form to the country and city in the url variable in this line in the Node code?
const url = `http://api.openweathermap.org/data/2.5/weather?q=city,country&APPID=${process.env.REACT_APP_WEATHER_API_KEY}`
UPDATE I have updated the WeatherForm component as follows:
import React from 'react';
import Axios from 'axios';
class WeatherForm extends React.Component {
constructor(props) {
super(props);
this.state = {
country: '',
city: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
const url = 'http://localhost:4000/weather';
const location = {
country: this.state.country,
city: this.state.city
}
Axios.post(url, location).then((res) => {
// what should I do here?
}).catch((e) => {
console.log(e);
})
}
updateLocation(e) {
this.setState({
country: e.target.value,
city: e.target.value
});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<p className="title">Weather</p>
<p className="subtitle">Check weather by city and country</p>
<div className="field">
<label className="label">Country</label>
<div className="control">
<input
className="input"
type="text"
placeholder="Type country name here"
onChange={e => this.updateLocation(e)} />
</div>
</div>
<div className="field">
<label className="label">City</label>
<div className="control">
<input
className="input"
type="text"
placeholder="Type city name here"
onChange={e => this.updateLocation(e)} />
</div>
</div>
<div className="field">
<div className="control">
<input
type='submit'
value='Search' />
</div>
</div>
</form>
)
}
}
export default WeatherForm
and I got error: POST http://localhost:4000/weather 404 (Not Found)
You want to use http requests to send the data to your backend. You can either use the native window.fetch API to send the data via a post request, or you can use a third-party library (I recommend axios).
The recommended way to send a post request on form submit in react is to store the field data in state (use the onChange prop on the input fields to update the state whenever the input value changes), and then use a handler function that gets fired when the submit button is clicked (use the onClick prop for your button element).
The handler function should get the current state (the form input field data) and pass it into the post request as the body.
When your express API receives the request, it can parse the data, and then fire off it's own API request to the openWeather API with that data as the url parameters.
UPDATE:
Updating due to updated question.
You don't have a post route defined in your express API. Therefore it won't accept post requests at the /weather route. What you need to do is write a handler that accepts post requests:
app.post('/weather', (req, res, next) => {
let { country, city } = req.body.data;
// here you send a post request to the weather API url
// to retrieve the results, then send them back
// to your react app to display them
}

Show added posts without refreshing page in React

I have been working on a personal project outside university, developing a blog.
Right now I'm trying to implement a "home page" where after a succesfull login, the user can post text, and right after that it appears under the Create post div you can see in the pic
This is what I have managed to accomplish so far:
This is the home page after login
Right now I can login, and post a new post which saves it in the database.
This is the home.js functional componenet which the user sees after a login:
import '../App.css';
import { useHistory } from "react-router-dom";
import React , {useState, useEffect} from 'react';
import jwt_decode from 'jwt-decode'
import logo from '../images/home-logo.png';
import {Col,Form,Input,Button,Card,CardTitle,Navbar,Nav,NavbarBrand} from 'reactstrap'
import { createPost,getUserPosts } from '../fucntions/user_functions'
function Home(){
var _decoded;
var _email;
let history = useHistory();
const[post_text,setPost] = useState('');
const handleChangePost = e =>{ setPost(e.target.value);};
function handlePost(e){
e.preventDefault();
const toPost = {
post :post_text, email :_email
}
createPost(toPost).then(res =>{
setPost('')
})
}
function getPosts() {
const container ={
email:_email
}
getUserPosts(container).then(res=>{
})
}
function handleLogout (e) {
e.preventDefault();
localStorage.removeItem('usertoken')
history.push(`/login`)
}
useEffect(() =>{
if (localStorage.getItem("usertoken") === null) {
history.push('/login')
} else {
const token = localStorage.usertoken
const user_email = localStorage.useremail
const decoded = jwt_decode(token)
_decoded = decoded;
_email = decoded.email
getPosts()
};
});
return (
<div className = "box">
<div>
<Navbar color="light" light expand="md">
<Nav>
<NavbarBrand type = "button" onClick = {handleLogout}>Logout</NavbarBrand>
</Nav>
</Navbar>
<div className = "wrapper">
<Card body outline color="secondary" className = "card-home " >
<CardTitle><img src={logo} alt="logo"></img>Create post</CardTitle>
<Form onSubmit = {handlePost}>
<Input id = "tx" name = "input1" type = "textarea" value = {post_text} placeholder="Enter your post here" onChange= {handleChangePost}></Input>
<br></br>
<Col sm={{ span: 10, offset: 5 }}>
<Button outline color="primary" type="submit">Post!</Button>
</Col>
</Form>
</Card>
</div>
</div>
</div>
)
}
export default Home;
I have implemented a getPosts method in the backend which gives back an array of the posts
router.post("/getPosts",
async (req, res) => {
const {email,} = req.body;
try {
let user = await User.findOne({email:email});
allPosts = user.posts
res.render('/home',{posts : hello})
} catch (e) {
console.error(e);
res.json("Error")
}
}
);
As you can see above, in the function getPosts(), the response is an Array of all the post's ids the user has posted, they are stored in the mongodb collection called "posts"
And after calling that function, I can iterate over them:
function getPosts() {
const container ={
email:_email
}
getUserPosts(container).then(res=>{
forEach(res.posts) {
}
})
}
I want to render all those posts live, so each time the user posts a new post, it will show right after the Create post div you can see in the picture, What's the best way?
Thanks
First define your posts collection state:
const [allPosts, setAllPosts] = useState([]);
Then every time you successfully save a post in the database, append it to that state:
function handlePost(e){
e.preventDefault();
const toPost = {
post :post_text, email :_email
}
createPost(toPost).then(res =>{
setPost('')
setAllPosts(allPosts.concat(toPost);
})
}
The same goes for getPosts:
function getPosts() {
const container ={
email:_email
}
getUserPosts(container).then(res=>{
setAllPosts(res.data); // <-- if the data is the same structure as the created before
})
}
Then you can render them in an example way:
return (
<div className = "box">
<div>
<Navbar color="light" light expand="md">
<Nav>
<NavbarBrand type = "button" onClick = {handleLogout}>Logout</NavbarBrand>
</Nav>
</Navbar>
<div className = "wrapper">
<Card body outline color="secondary" className = "card-home " >
<CardTitle><img src={logo} alt="logo"></img>Create post</CardTitle>
<Form onSubmit = {handlePost}>
<Input id = "tx" name = "input1" type = "textarea" value = {post_text} placeholder="Enter your post here" onChange= {handleChangePost}></Input>
<br></br>
<Col sm={{ span: 10, offset: 5 }}>
<Button outline color="primary" type="submit">Post!</Button>
</Col>
</Form>
<div>
{
allPosts.map(post => {
return <div><div>email: {post.email}</div><div>post: post.post</div></div>
})
}
</div>
</Card>
</div>
</div>
</div>
)
Feel free to change the HTML structure, so it matches your design

Axios POST request doesn't work

I'm using Axios to send POST request to Node.js server but it doesn't work. Do you have any idea how to resolve it?
My code is shown below:
server.js:
app.post('/registration', (req, res) => {
console.log(req.body);
});
my class:
export default class Registration extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {}
}
handleSubmit (e) {
e.preventDefault;
axios.post('/registration', {name: document.getElementById('name') }).then(res => {
console.log(res);
})
}
render() {
return (<form className="registrationForm">
<input type="text" name="name" id="name" required="required" placeholder="name"/>
<br/>
{/*<input type="text" name="email" required="required" placeholder="email"/>
<br/>
<input type="number" name="phoneNumber" required="required" placeholder="phoneNo"/>
<br/>
<input type="password" name="password" required="required" placeholder="pass"/>
<br/> */}
<button className="registerButton" onClick={this.handleSubmit}>register</button>
</form>)
};
}
You have various problems in your code
preventDefault is method. You need to call it
I doubt you want to send DOM element to the server
You want to handle network failure using catch
Corrected handleSubmit should look like this
handleSubmit (e) {
e.preventDefault(); // NB
const data = {name: document.getElementById('name').value /* NB */ };
axios.post('/registration', data).then(res => {
console.log(res);
}).catch(console.error) // now you could see what the actual problem is
}
Also it is generally not adviced to use DOM lookup methods in your React up. You should better keep a ref to the input.
<input ... ref={input => this.name = input}/>
const data = {name: this.name.value };
The problem was for just a single line that I didn't write in my server side application.
The only thing to check is to put the following line after requiring the body-parser in your file.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

Resources