I am trying to learn to code by following an eCommerce product tutorial guide in React.
I have finished setting up the product page and checkout.
My goal now is to extract the cart: [] along with the checkout total into a form using EmailJS.
import React from 'react';
import emailjs from 'emailjs-com';
import './form.css';
import { ProductConsumer } from '../../context'
import CartList from './CartList'
import CartColumns from "./CartColumns"
import CartTotals from './CartTotals'
import Title from "../Title";
import { ProductProvider } from "../../context"
import CartItem from './CartItem'
export default function CheckOut() {
function sendEmail(e) {
alert("Your order has been placed!")
e.preventDefault();
emailjs.sendForm('gmail', 'template_3DAGPnwJ', e.target, 'user_m49ol85bvoqFluF8IKatG')
.then((result) => {
console.log(result.text);
}, (error) => {
console.log(error.text);
});
}
let Order = "cat"
return (
<div>
<ProductConsumer>
{value => {
return(
<div>
<Title name="Check" title="Out"/>
<CartColumns />
<CartList value={value} />
<CartTotals value={value}/>
</div>
)
}
}
</ProductConsumer>
<form className="contact-form" onSubmit={sendEmail}>
<label>Contact Number eg. 12345678 </label>
<input type="tel" name="contact_number" placeholder="12345678" pattern="[0-9]{8}" required />
<label>Name</label>
<input type="name" name="user_name" required />
<label>Email</label>
<input type="email" name="user_email" required />
<label>Address Line 1</label>
<input type="address-line1" name="user_address1" required />
<label>Address Line 2</label>
<input type="address-line2" name="user_address2" />
<label>Message (eg. Special Requests)</label>
<textarea name="message_html" />
<input type="submit" value="Place Order" />
<textarea name="proudct order" value={Order}/>
</form>
</div>
);
}
So far the textarea is showing "cat" from the let Order = "cat".
I would like to put the array information from CartList and CartTotal into Order. Once the client submits the form, I will have their name email address and the order they collected in the cart. This will be emailed to me via EmailJS.
import React from 'react';
import emailjs from 'emailjs-com';
import './form.css';
import { ProductConsumer } from '../../context';
import CartList from './CartList';
import CartTotals from './CartTotals';
import Title from '../Title';
export default function CheckOut() {
function sendEmail(e) {
alert('Your order has been placed! Our staff will get in touch with you to confirm your order and to process payment.');
e.preventDefault();
emailjs
.sendForm(
'gmail',
'xxxxxxxxx',
e.target,
'xxxxxxxxx'
)
.then(
(result) => {
console.log(result.text);
},
(error) => {
console.log(error.text);
}
);
}
const Form = ({ context }) => {
let Order = context.cart.map((item) => `ID: ${item.id} \n${item.title} \t $ ${item.price} \t Qty:${item.count} \n\n`);
let OrderSubtotal = context.cartSubtotal ;
let OrderServce = context.cartService;
let OrderShipping = context.cartShipping;
let OrderTotal = context.cartTotal;
return (
<form className="contact-form" onSubmit={sendEmail}>
<label>Contact Number eg. 12345678 </label>
<input
type="tel"
name="contact_number"
placeholder="12345678"
pattern="[0-9]{8}"
required
/>
<label>Name</label>
<input type="name" name="user_name" required />
<label>Email</label>
<input type="email" name="user_email" required />
<label>Address Line 1</label>
<input type="address-line1" name="user_address1" required />
<label>Address Line 2</label>
<input type="address-line2" name="user_address2" />
<label>Message (eg. Special Requests)</label>
<textarea name="message_html" />
<input type="submit" value="Place Order" />
<textarea style={{display:"none"}} name="Order" defaultValue={Order} />
<textarea style={{display:"none"}} name="OrderSubtotal" defaultValue={OrderSubtotal} />
<textarea style={{display:"none"}} name="OrderServce" defaultValue={OrderServce} />
<textarea style={{display:"none"}} name="OrderShipping" defaultValue={OrderShipping} />
<textarea style={{display:"none"}} name="OrderTotal" defaultValue={OrderTotal} />
</form>
);
};
return (
<div>
<ProductConsumer>
{(value) => {
return (
<div>
<Title name="Check" title="Out" />
<CartList value={value} />
<CartTotals value={value} />
</div>
);
}}
</ProductConsumer>
<ProductConsumer>
{(context) => <Form context={context} />}
</ProductConsumer>
</div>
);
}
The answer.
import { ProductConsumer } from '../../context';
let Order = context.cart.map((item) => ID: ${item.id} \n${item.title} \t $ ${item.price} \t Qty:${item.count} \n\n);
which is then exported to emailJS in the textarea.
<textarea style={{display:"none"}} name="Order" defaultValue={Order} />
and rendered
<ProductConsumer>
{(context) => <Form context={context} />}
</ProductConsumer>
Answer suggested by Metafield on Scrimba Discord Community. Thanks a ton!
You can and should use state to update any variable in a React component! CheckOut is a functional component. To use state in a functional component you have to use a hook, which is useState.
To get value from CartList and CartTotal and update Order, pass callback functions from CheckOut component.
Please check the following code, hope it helps.
import React, { useState } from "react";
import emailjs from "emailjs-com";
import "./form.css";
import { ProductConsumer } from "../../context";
import CartList from "./CartList";
import CartColumns from "./CartColumns";
import CartTotals from "./CartTotals";
import Title from "../Title";
import { ProductProvider } from "../../context";
import CartItem from "./CartItem";
export default function CheckOut() {
// Order default value is empty array. i.e. no orders
// updateOrder will update Order
const [Order, updateOrder] = useState([]);
// cartTotal to store CartTotals value
// updateCartTotal will update cartTotal
const [cartTotal, updateCartTotal] = useState(0);
function sendEmail(e) {
alert("Your order has been placed!");
e.preventDefault();
// You can access Order and cartTotal here.
console.log(Order, cartTotal);
emailjs
.sendForm(
"gmail",
"template_3DAGPnwJ",
e.target,
"user_m49ol85bvoqFluF8IKatG"
)
.then(
(result) => {
console.log(result.text);
},
(error) => {
console.log(error.text);
}
);
}
return (
<div>
<ProductConsumer>
{(value) => {
return (
<div>
<Title name="Check" title="Out" />
<CartColumns />
{/* updateOrder is a callback passed by prop */}
<CartList value={value} onCartUpdate={updateOrder} />
{/* updateCartTotal is a callback passed by prop */}
<CartTotals value={value} onCartTotalUpdate={updateCartTotal} />
</div>
);
}}
</ProductConsumer>
<form className="contact-form" onSubmit={sendEmail}>
<label>Contact Number eg. 12345678 </label>
<input
type="tel"
name="contact_number"
placeholder="12345678"
pattern="[0-9]{8}"
required
/>
<label>Name</label>
<input type="name" name="user_name" required />
<label>Email</label>
<input type="email" name="user_email" required />
<label>Address Line 1</label>
<input type="address-line1" name="user_address1" required />
<label>Address Line 2</label>
<input type="address-line2" name="user_address2" />
<label>Message (eg. Special Requests)</label>
<textarea name="message_html" />
<input type="submit" value="Place Order" />
<textarea name="proudct order" value={Order} />
</form>
</div>
);
}
I see your input fields are uncontrolled! React suggests to use a controlled component for form input. You can get idea from here - https://reactjs.org/docs/forms.html#controlled-components
Also, since you are learning React I suggest you read about two core parts of React. They are props and state.
For working with forms you can check this popular form handling library for react. https://formik.org/
Related
I am learning react and I have a component which as a 2 input fields and a button, at the moment, clicking on the button will display a message in console log, but when the button is clicked it displays a popup Leave site?, Changes that you made may not be saved.
this is my code in this component
import React, { useRef, useState, Component } from 'react'
import { useAuthState } from 'react-firebase-hooks/auth';
import { useCollectionData } from 'react-firebase-hooks/firestore';
import { signOut } from 'firebase/auth';
class InfoLgnTest extends Component {
render() {
this.state = {
user: null
}
return (
<div>
<div className="App">
<SignInWithEmailPassword />
</div>
</div>
)
}
}
function SignInWithEmailPassword() {
const emailRef = useRef()
const passwordRef = useRef()
const signIn = () => {
console.log("InfoLgnTest singin clicked")
}
return (
<>
<div className="form">
<form>
<div className="input-container">
<label>Username </label>
<input
name="email"
type="text"
ref={emailRef}
required
placeholder ="something#gmail.com"
/>
</div>
<div className="input-container">
<label>Password </label>
<input
type="text"
name="pass"
ref={passwordRef}
required
placeholder ="..."
/>
</div>
<div className="button-container">
<input type="submit" onClick={signIn}/>
</div>
</form>
</div>
</>
)
}
export default InfoLgnTest
This code has a form, by default form send data as a request on the same page, for resolve this:
Add onSubmit to form,
call preventDefault method from event
call the function signIn
Change <input type="submit" ... /> to <button type="submit">Send</button>
function SignInWithEmailPassword() {
const emailRef = useRef()
const passwordRef = useRef()
const signIn = () => {
console.log("InfoLgnTest singin clicked")
}
// new function to handle submit
const submitForm = (event) => {
event.preventDefault();
signIn();
}
return (
<>
<div className="form">
{/* Add onSubmit */}
<form onSubmit={submitForm}>
<div className="input-container">
<label>Username </label>
<input
name="email"
type="text"
ref={emailRef}
required
placeholder ="something#gmail.com"
/>
</div>
<div className="input-container">
<label>Password </label>
<input
type="text"
name="pass"
ref={passwordRef}
required
placeholder ="..."
/>
</div>
<div className="button-container">
{/* Change input to button */}
<button type="submit">Send</button>
</div>
</form>
</div>
</>
)
}
I want to print the information I get from the form on the info page when I press the send button. However, when the information is taken as default value, it does not come to the info page.
Information from the form needs to be printed on the Info page
My App.js
import React, { useState } from 'react'
import './App.css';
import { Route, Routes } from 'react-router-dom'
import Info from './Info';
import Form from './Form';
function App() {
const [form, setForm] = useState({ name: "", city: "", birth: "", color: "", address: "" });
const handleChange = (event) => {
setForm({ ...form, [event.target.name]: event.target.value })
}
return (
<div className="App">
<h2>Enter your informations</h2>
<Routes>
<Route path="/" exact element={<Form form={form} onChange={handleChange}/>}></Route>
<Route path="/info" element={<Info form={form}/>}></Route>
</Routes>
</div>
);
}
export default App;
My Form.js
import React from 'react'
import { Link } from 'react-router-dom'
function Form({ form, HandleChange }) {
return (
<div>
<form>
<input type="text" name="isim" defaultValue={form.name} onChange={HandleChange} placeholder="Enter your name" />
<select name="city" defaultValue={form.city} onChange={HandleChange}>
<option defaultValue="">Enter your city</option>
<option defaultValue="Ankara">Ankara</option>
<option defaultValue="İstanbul">İstanbul</option>
<option defaultValue="İzmir">İzmir</option>
</select>
<input type="date" name="birth" defaultValue={form.birth} onChange={HandleChange} />
<input type="color" name="color" defaultValue={form.color} onChange={HandleChange} />
<textarea name="address" placeholder="Enter your address" cols="20" rows="5" defaultValue={form.adsress} onChange={HandleChange}></textarea>
<Link to="/info"><button>Send</button></Link>
</form>
</div>
)
}
export default Form
My Info.js
import React from 'react'
import { Link} from 'react-router-dom'
function Info({form}) {
return (
<div>
<p>Name: {form.name} </p>
<p>City: {form.city}</p>
<p>Birthday: {form.birth}</p>
<p>Color: {form.color}</p>
<p>Address: {form.address}</p>
<Link to="/"><button>Back</button></Link>
</div>
)
}
export default Info
First issue is that the Form component is passed an onChange prop but is destructuring a HandleChange prop.
<Route
path="/"
element={<Form form={form} HandleChange={handleChange} />}
/>
Fix this to pass a consistently named prop.
<Route
path="/"
element={<Form form={form} onChange={handleChange} />}
/>
The second issue from what I can see of the code it seems the button element is submitting the form and since the default form action is not prevented the page/app is reloading. HTML button elements have type="submit" by default if not specified.
Make sure that the name attribute for the "name" field matches the React state key. In other words, ensure it is name="name" instead of name="isim".
Additionally, all these form fields should technically be controlled inputs since you are attaching an onChange handler to each. Controlled inputs use the value prop instead of the defaultValue prop.
You've a couple options:
Declare the button to not be a form submit button.
function Form({ form, onChange }) {
return (
<div>
<form>
<input
type="text"
name="name"
value={form.name}
onChange={onChange}
placeholder="Enter your name"
/>
<select name="city" value={form.city} onChange={onChange}>
<option disabled value="">Enter your city</option>
<option value="Ankara">Ankara</option>
<option value="İstanbul">İstanbul</option>
<option value="İzmir">İzmir</option>
</select>
<input
type="date"
name="birth"
value={form.birth}
onChange={onChange}
/>
<input
type="color"
name="color"
value={form.color}
onChange={onChange}
/>
<textarea
name="address"
placeholder="Enter your address"
cols="20"
rows="5"
value={form.adsress}
onChange={onChange}
></textarea>
<Link to="/info">
<button type="button">Send</button>
</Link>
</form>
</div>
);
}
Declare a form submit handler and prevent the default form action and issue the imperative navigation action.
import { useNavigate } from 'react-router-dom';
function Form({ form, onChange }) {
const navigate = useNavigate();
const submitHandler = (e) => {
e.preventDefault();
navigate("/info");
};
return (
<div>
<form onSubmit={submitHandler}>
<input
type="text"
name="name"
value={form.name}
onChange={onChange}
placeholder="Enter your name"
/>
<select name="city" value={form.city} onChange={onChange}>
<option disabled value="">Enter your city</option>
<option value="Ankara">Ankara</option>
<option value="İstanbul">İstanbul</option>
<option value="İzmir">İzmir</option>
</select>
<input
type="date"
name="birth"
value={form.birth}
onChange={onChange}
/>
<input
type="color"
name="color"
value={form.color}
onChange={onChange}
/>
<textarea
name="address"
placeholder="Enter your address"
cols="20"
rows="5"
value={form.adsress}
onChange={onChange}
></textarea>
<button type="submit">Send</button>
</form>
</div>
);
}
In either case you'll still want to be explicit about the button type for readability and maintainability reasons.
Demo
I am having the hardest time trying to get an alert to pop up after clicking on a button. I have been working on it for quite a while but can't seem to figure out where I am wrong. As per the code below, everything works fine, but the Alert will not show up. Any suggestions or advice would be greatly appreciated.
import React, {useState} from 'react';
import { Redirect } from 'react-router-dom'
import { MDBContainer, MDBRow, MDBCol, MDBBtn, MDBIcon } from 'mdbreact';
import NavBar from "./NavBar";
import Footer from "./Footer";
import emailjs from 'emailjs-com';
import{ init } from 'emailjs-com';
function Contact() {
const [alertVisibility , setAlertVisibility ] = useState(false)
function sendEmail(e) {
e.preventDefault();
emailjs.sendForm('hidden', 'hidden', e.target, 'hidden')
.then((result) => {
setAlertVisibility(true);
if (setAlertVisibility === true ) {
return(
<button class="closebtn">
Great, your message has been sent.</button>
)
}
});
e.target.reset();
};
return (
<div>
<NavBar />
<br />
<br />
<br />
<br />
<br />
<MDBContainer>
<MDBRow className = "formcontainer">
<MDBCol md="6">
<form onSubmit={sendEmail}>
<p className="h4 text-center mb-4">Fill in the information below to contact me!</p>
<br />
<label htmlFor="defaultFormContactNameEx" className="grey-text" > Your name </label>
<input required type="text" id="defaultFormContactNameEx" className="form-control" name="user_name" />
<br />
<label htmlFor="defaultFormContactEmailEx" className="grey-text"> Your email </label>
<input required type="email" id="defaultFormContactEmailEx" className="form-control" name="user_email"/>
<br />
<label htmlFor="defaultFormContactSubjectEx" className="grey-text"> Subject</label>
<input required type="text" id="defaultFormContactSubjectEx" className="form-control" name="user_subject" />
<br />
<label htmlFor="defaultFormContactMessageEx" className="grey-text"> Your message </label>
<textarea required type="text" id="defaultFormContactMessageEx" className="form-control" rows="7" name="message"/>
<div className="text-center mt-4">
<MDBBtn color="warning" outline type="submit">
Send
<MDBIcon far icon="paper-plane" className="ml-2" />
</MDBBtn>
</div>
</form>
</MDBCol>
</MDBRow>
</MDBContainer>
<br />
<br />
<Footer />
</div>
);
};
export default Contact;
You are updating alertVisibility with the useState function, so your check should be for
if (alertVisibility === true) {...
You can use
SweetAlert2
import React, {useState} from 'react';
import { Redirect } from 'react-router-dom'
import { MDBContainer, MDBRow, MDBCol, MDBBtn, MDBIcon } from 'mdbreact';
import NavBar from "./NavBar";
import Footer from "./Footer";
import emailjs from 'emailjs-com';
import{ init } from 'emailjs-com';
import Swal from 'sweetalert2';
function Contact() {
const form = useRef();
const sendEmail = (e) => {
e.preventDefault();
emailjs
.sendForm(
EMAILJS_SERVICE_ID,
EMAILJS_TEMPLATE_ID,
form.current,
EMAILJS_PUBLIC_KEY
)
.then(
(result) => {
Swal.fire({
icon:"success",
title:`<h5 style='color:green'>Message Sent Successfully</h5>`,
confirmButtonColor: 'green',
})
},
(error) => {
Swal.fire({
icon:"error",
title:`<h5 style='color:red'>Ooops, something went wrong</h5>`,
confirmButtonColor: 'red',
})
}
);
e.target.reset()
};
return(
<div>
</div>
)
}
export default Contact;
import React from 'react';
import ReactTooltip from 'react-tooltip';
const DataInputBox = () => {
return (
<div>
<ReactTooltip id="title required"effect="solid" place="bottom" >
"Title required"
</ReactTooltip>
<input
type="text"
data-tip
data-for="title required"
placeholder="Type title"
name="title"
/>
</div>
);
}
export default DataInputBox;
Not able to get tooltip at bottom start of input box.Can anyone please help me to get tooltip at cursor start.
You need to add ref to input element and pass this ref to ReactTooltip.show() method.
const DataInputBox = () => {
const inputRef = React.useRef(null)
React.useEffect(() => {
ReactTooltip.show(inputRef.current);
}, []);
return (
<div>
<ReactTooltip id="title required" efect="solid" place="bottom">
"Title required"
</ReactTooltip>
<input
type="text"
ref={inputRef}
data-tip
data-for="title required"
placeholder="Type title"
name="title"
/>
</div>
);
}
In this code, I want to upload the radio button value and store it in firebase database.I want use the simplest way to solve that. I see other code will use constructor but I do not know whether I can use simpler way to solve it. how can I do that ?
import React, { useState } from "react";
import ReactPlayer from "react-player";
import Array from "../Array";
import firebase from "firebase";
export default () => {
const [selected, setSelected] = useState("");
return (
<div>
<div className="video">
<ReactPlayer url={Array[0]} playing />
</div>
<label>Guess whether this video is fake or real ?</label> <br />
<label>
<input
type="radio"
name="answer"
value="real"
onChange={e => setSelected(e.target.value)}
/>
real
</label>
<label>
<input
type="radio"
name="answer"
value="fake"
onChange={e => setSelected(e.target.value)}
/>
fake
</label>
</div>
);
};
I would do it like this:
import React from "react";
....
export default () => {
const [state, setState] = React.useState({});
const handleInputChange = e => {
const { name, value } = e.target;
console.log(name, value);
setState(state => {
const newState = { ...state, [name]: value }
//post newState to firebase
return newState
});
};
return (
<div>
<div className="video">
<ReactPlayer url={Array[0]} playing />
</div>
<div>{JSON.stringify(state)}</div>
<label>Guess whether this video is fake or real ?</label> <br />
<label>
<input
type="radio"
name="answer1"
value="real"
onChange={handleInputChange}
/>
real
</label>
<label>
<input
type="radio"
name="answer2"
value="fake"
onChange={handleInputChange}
/>
fake
</label>
</div>
);
};