Pass data between two react components in MERN stack - javascript

I am trying to build an E commerce web app where I handle the payments. First I need to take shipping information. I have successfully inserted data into mongo but I could not pass the data into another js right away from a single click.
Here is my create address page.
import React, { Component } from 'react'
import '../App.css'
import axios from 'axios'
export default class CreateAddress extends Component {
constructor(props) {
super(props);
this.state = {
address: "",
city: "",
phoneNo:"",
postalCode: "",
country: ""
}
}
handleInputChange = (e)=>{
const {name,value} = e.target;
this.setState({
...this.state,
[name]:value
})
}
onSubmit = (e)=>{
e.preventDefault();
const {address,city,phoneNo,postalCode,country} = this.state;
const data = {
address: address,
city: city,
phoneNo: phoneNo,
postalCode: postalCode,
country: country
}
console.log(data)
axios.post("http://localhost:5000/address/save",data).then((res)=>{
if(res.data.success){
this.setState(
{address: "",
city: "",
phoneNo:"",
postalCode: "",
country: ""}
)
alert("Shipping info added successfully");
}
})
}
render() {
return (
<div className="row wrapper">
<div className="col-10 col-lg-5">
<form className="shadow-lg" >
<h1 className="mb-4">Shipping Info</h1>
<div className="form-group">
<label htmlFor="address_field">Address</label>
<input
type="text"
id="address_field"
className="form-control"
name='address'
value={this.state.address}
onChange={this.handleInputChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="city_field">City</label>
<input
type="text"
id="city_field"
className="form-control"
name='city'
value={this.state.city}
onChange={this.handleInputChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="phone_field">Phone No</label>
<input
type="phone"
id="phone_field"
className="form-control"
name='phoneNo'
value={this.state.phoneNo}
onChange={this.handleInputChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="postal_code_field">Postal Code</label>
<input
type="number"
id="postal_code_field"
className="form-control"
name='postalCode'
value={this.state.postalCode}
onChange={this.handleInputChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="country_field">Country</label>
<input
type = "text"
id="country_field"
className="form-control"
name='country'
value={this.state.country}
onChange={this.handleInputChange}
required
/>
</div>
<button
id="shipping_btn"
type="submit"
className="btn btn-block py-3"
onClick={this.onSubmit}
>
SAVE
</button>
<button id="shipping_btn" type="submit" className = "btn btn-block py-3"><a href = "/confirm" style={{textDecoration:'none',color:'white'}}>PROCEED</a></button>
</form>
</div>
</div>
)
}
}
Once I clicked the PROCEED button the data should direct into /confirm page

You can use react-router to move between different routes in React. Also, in your code when you are sending a post request on onSubmit method, you are clearing the state information, which I think needs to be handled in the confirm page since you need the data in that page also.
Here's a codesandbox demonstrating how you can send the information to another page, using react-router. I've used the functional components, but the concepts works for the class based components also.
note: also you should use onSubmit handler of the form to submit values rather than using the onClick method of the button. Otherwise you force users to click the button to submit the form.

You can use history API to pass data as a URL and grab it into confirm page.
history.push(`/confirmPage?city=${city}&postalCode=${postalCode}`)
in confirmPage.js you will get that data as props
export default class ConfirmPage extends Component{
render(){
const {city, postalCode } = this.props.location.query;
return (
<div>{postalCode}</div>
)
}

Related

Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

I'm trying to create a sign up form with an input for a users address. The address input uses the google autocomplete address api.
I'd like to be able to keep it as a Formik field, so I can use Yup validation on it.
The address input component looks like
// Google.jsx
import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
/* global google */
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.autocompleteInput = React.createRef();
this.autocomplete = null;
this.handlePlaceChanged = this.handlePlaceChanged.bind(this);
}
componentDidMount() {
this.autocomplete = new google.maps.places.Autocomplete(this.autocompleteInput.current,
{"types": ["address"]});
this.autocomplete.addListener('place_changed', this.handlePlaceChanged);
}
handlePlaceChanged(){
const place = this.autocomplete.getPlace();
console.log(place);
}
render() {
return (
<Field ref={this.autocompleteInput} id="autocomplete" type="text" name="address" placeholder="" />
);
}
}
export default SearchBar;
And my Form component looks like:
import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
import SearchBar from "./Google";
const LoginSchema = Yup.object().shape({
fName: Yup.string().required("Please enter your first name"),
address: Yup.string().required("invalid address"),
});
class Basic extends React.Component {
render() {
return (
<div className="container">
<div className="row">
<div className="col-lg-12">
<Formik
initialValues={{
fName: "",
postal: "",
}}
validationSchema={LoginSchema}
onSubmit={(values) => {
console.log(values);
console.log("form submitted");
}}
>
{({ touched, errors, isSubmitting, values }) =>
!isSubmitting ? (
<div>
<div className="row mb-5">
<div className="col-lg-12 text-center">
<h1 className="mt-5">LoKnow Form</h1>
</div>
</div>
<Form>
<div className="form-group">
<label htmlFor="fName">First Name</label>
<Field
type="text"
name="fName"
className={`mt-2 form-control
${touched.fName && errors.fName ? "is-invalid" : ""}`}
/>
<ErrorMessage
component="div"
name="fName"
className="invalid-feedback"
/>
</div>
<div className="form-group">
<label htmlFor="address">Address</label>
<Field name="address" component={SearchBar} placeholder="" />
<ErrorMessage
component="div"
name="address"
className="invalid-feedback"
/>
</div>
<button
type="submit"
className="btn btn-primary btn-block mt-4"
>
Submit
</button>
</Form>
</div>
) : (
<div>
<h1 className="p-3 mt-5">Form Submitted</h1>
<div className="alert alert-success mt-3">
Thank for your connecting with us.
</div>
</div>
)
}
</Formik>
</div>
</div>
</div>
);
}
}
export default Basic;
This returns an error of "Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?".
Which is coming from my address input component at: <Field ref={this.autocompleteInput} id="autocomplete" type="text" name="address" placeholder="" />
Everything else is working, I just need to get past this last hurdle and I'll be good from here.
I will begin looking into the docs, but I'm unfortunately in a rush to get this done so I figured I'd try my luck here!
Any help is greatly appreciated! Thank you!
The Field component does allow you to get access to the underlying element, but not via ref. You need to pass it to innerRef instead.
<Field innerRef={this.autocompleteInput} id="autocomplete" type="text" name="address" placeholder=""/>
See documentation of Field here: https://formik.org/docs/api/field#innerref

React + EmailJS is sending me tons of emails in onClick method

I'm new to web development in general but I'm trying to create my own Contact page on my website and I'm having trouble. I'm using React, Gatsby, and Emailjs. I have my form set up so that the inputs are passed into the state onChange. Then I have a "send message" button that should send an email using EmailJS using the tokens and the state vars. This does all work and the email sends successfully, but it's sending dozens of emails. I believe what's happening is it's calling sendEmail every time the state is set and the DOM re-renders, or basically once for each character that's input into the fields, but I don't know why.
Bonus points if you can help me figure out why pressing the send message button also sends me to a 404 /# route on my site.
import React from 'react'
import emailjs from 'emailjs-com
class Main extends React.Component {
constructor(){
super()
this.state = {
fromName:'',
message:'',
fromEmail:''
}
}
render() {
return (
<div ...
>
...
<article>
...
<form method="post" action="#">
<div className="field half first">
<label htmlFor="name">Name</label>
<input type="text" name="name" id="name" value={this.state.fromName} onChange={e => this.setState({fromName: e.target.value})}/>
</div>
<div className="field half">
<label htmlFor="email">Email</label>
<input type="text" name="email" id="email" value={this.state.fromEmail} onChange={e => this.setState({fromEmail: e.target.value})}/>
</div>
<div className="field">
<label htmlFor="message">Message</label>
<textarea name="message" id="message" rows="4" value={this.state.message} onChange={e => this.setState({message: e.target.value})}
placeholder = "..."></textarea>
</div>
<ul className="actions">
<li>
<input type="submit" value="Send Message" className="special" onClick={this.sendEmail()}/>
</li>
</ul>
</form>
</article>
</div>
)
}
sendEmail() {
const serviceId='...'
const templateId='...'
const userId='...'
emailjs.send(serviceId, templateId, this.state, userId)
}
}
export default Main
The issue is that you never followed the emailjs documentation well and you never prevented the default form action.
According to the emailjs documentation you should have set the onClick function with the send email function (without invoking it) on the form's opening tag NOT on your submit button. (but the button is still necessary so that it can send the sign that the form needs to be submitted). You also invoked the sendEmail function which is inappropriate and leads to problems.
You must also add event as a parameter in your sendEmail function when creating this function. Then inside the sendEmail function call the event.preventDefault() function .
import React from 'react'
import emailjs from 'emailjs-com
class Main extends React.Component {
constructor(){
super()
this.state = {
fromName:'',
message:'',
fromEmail:''
}
}
render() {
return (
<div>
<article>
<form method="post" onClick={this.sendEmail} action="#">
<div className="field half first">
<label htmlFor="name">Name</label>
<input type="text" name="name" id="name" value={this.state.fromName} onChange={e => this.setState({fromName: e.target.value})}/>
</div>
<div className="field half">
<label htmlFor="email">Email</label>
<input type="text" name="email" id="email" value={this.state.fromEmail} onChange={e => this.setState({fromEmail: e.target.value})}/>
</div>
<div className="field">
<label htmlFor="message">Message</label>
<textarea name="message" id="message" rows="4" value={this.state.message} onChange={e => this.setState({message: e.target.value})}
placeholder = "..."></textarea>
</div>
<ul className="actions">
<li>
<input type="submit" value="Send Message" className="special"/>
</li>
</ul>
</form>
</article>
</div>
)
}
sendEmail(event) {
event.preventDefault();
const serviceId='...'
const templateId='...'
const userId='...'
emailjs.send(serviceId, templateId, this.state, userId)
.then((result) => {
console.log(result.text);
}, (error) => {
console.log(error.text);
});
}
}
export default Main

How can I force re-render input fields in a form in React?

I am trying to create a form where the user can edit the information of a contact from a database and then save those changes. The form correctly loads and saves the information from the database, but when componentDidMount() loads the current information into the form fields, the fields don't re-render, causing both the template text (ie. Name, Email, etc) and the information loaded from the database to display on top of each other. Clicking any of the input fields causes the field that was clicked to render properly with the text displaying above the input field, rather than over the loaded text.
How can I force the form to rerender each field after the data has been loaded properly in componentDidMount()? I've tried forceUpdate() but it didn't work, and I haven't been able to find anyone else with this issue. I suspect it has something to do with me using the bootstrap styles wrong, but even after reading all of bootstrap's documentation I couldn't find anything related to my issue.
Here are a couple of screenshots to show the issue.
The form right after loading
The form after clicking the 'Name' field
Here's the relevant code for the component.
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
role: "",
phone: "",
errors: {}
}
}
// Initializer: Copy contact's current data to state
componentDidMount() {
axios.get("/api/contacts/get/" + this.props.match.params.id)
.then(res => {
this.setState({ name: res.data.name });
this.setState({ email: res.data.email });
this.setState({ role: res.data.role });
this.setState({ phone: res.data.phone });
})
.catch(err => console.log(err));
this.forceUpdate();
}
// Change handler: Modify values when changed
onChange = e => {
this.setState({ [e.target.id]: e.target.value });
}
// Submit handler: Save changes to database
onSubmit = e => {
e.preventDefault();
const contact = {
name: this.state.name,
email: this.state.email,
role: this.state.role,
phone: stripNumber(this.state.phone)
};
// Post modified contact to database, then navigate back to Manage Contacts Dashboard
axios.post("/api/admin/contacts/update/" + this.props.match.params.id, contact);
window.location = PREVIOUS_URL;
}
render() {
const { errors } = this.state;
return (
<div>
<Link to={PREVIOUS_URL} className="btn-flat waves-effect">
<i className="material-icons left">keyboard_backspace</i> Back to Manage Contacts
</Link>
<h3>Edit contact</h3>
<form noValidate onSubmit={this.onSubmit}>
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.name}
error={errors.name}
id="name"
type="text"
className={classnames("", { invalid: errors.name })}
/>
<label htmlFor="name">Name</label>
<span className="red-text">{errors.name}</span>
</div>
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.email}
error={errors.email}
id="email"
type="email"
className={classnames("", { invalid: errors.email || errors.emailnotfound })}
/>
<label htmlFor="email">Email</label>
<span className="red-text">{errors.email}{errors.emailnotfound}</span>
</div>
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.role}
error={errors.role}
id="role"
type="text"
className={classnames("", { invalid: errors.role })}
/>
<label htmlFor="role">Role</label>
<span className="red-text">{errors.role}</span>
</div>
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.phone}
error={errors.phone}
id="phone"
type="tel"
className={classnames("", { invalid: errors.phone })}
/>
<label htmlFor="phone">Phone Number</label>
<span className="red-text">{errors.phone}</span>
</div>
<div className="form-group">
<input type="submit" value="Submit changes" className="btn btn-primary" />
<Link to={PREVIOUS_URL} className="btn btn-danger"> Cancel </Link>
</div>
</form>
</div>
)
}
}
Have you tried doing the data fetch inside componentWillMount()?

how to login in ReactJs?

how to login in ReactJS?
i am probably new to reactJS, so i have no idea about how to proper login in Reactjs. i tried something like below but that's not proper working.
it would be great if anybody could help me out for what i'm trying to do. thank you so much in advance.
login-api end point: http://localhost:8000/api/login
export default class App extends Component{
constructor(props) {
super(props);
this.state ={
username: "",
password: "",
}
this.onFormSubmit = this.onFormSubmit.bind(this)
}
onFormSubmit(values){
console.log(values);
const formData = new FormData();
formData.append("username", values.username);
formData.append("password", values.password);
const options = {
method: 'POST',
body: formData
};
fetch('http://localhost:8000/api/login', options).then(() => {
this.props.history.replace('/home')
}).catch((error) => {
console.log(this.props.state)
})
};
render(){
return(
<div>
<section class="login_part section_padding ">
<div class="login_part_form">
<Form onFinish={this.onFormSubmit}>
<div class="col-md-12 form-group p_star">
<Form.Item name="username">
<input type="text" class="form-control" placeholder="Username" required />
</Form.Item>
</div>
<div class="col-md-12 form-group p_star">
<Form.Item name="password">
<input type="password" class="form-control"
placeholder="Password" required />
</Form.Item>
</div>
<div class="col-md-12 form-group">
<button type="submit" value="submit" class="btn_3">
log in
</button>
</div>
</Form>
</div>
</section>
</div>
)
}
}
So, looking at your code, i do not really see you changing your state anywhere.
Looks like you lack some simple handlers, that will use setState method to modify the password and username in your state. You want to probably do it onChange of an input.
After that in your onFormSubmit function you want to get the values from your state and send those to the server.

A component is changing an uncontrolled input of type email to be controlled. Input elements should not switch from uncontrolled to controlled

I am getting this error, I am new in react. I have seen other posts but still could not resolve the issue. help will be appreciated. thanks
Warning: A component is changing an uncontrolled input of type email to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info
in input (created by Edit)
in div (created by Edit)
in div (created by Edit)
in form (created by Edit)
in div (created by Edit)
in Edit
import React from 'react'
import ReactDom from 'react-dom'
export default class Edit extends React.Component{
constructor(){
super();
this.state={
name: '',
email: '',
password: '',
};
}
componentWillMount(){
let id=this.props.id;
axios.get('/api/users/'+id).then(response => {
var user= response.data;
this.setState({
name: user.name,
email: user.email,
})
}).catch(error => {
console.log(error)
})
}
handleNameChange(e){
this.setState({
name: e.target.value
})
}
handleEmailChange(e){
this.setState({
email: e.target.value
})
}
handlePasswordChange(e){
this.setState({
password: e.target.value
})
}
handleSubmit(e){
e.preventDefault();
console.log(this.state)
axios.post('/api/users',this.state).then(response => {
console.log(response)
}).then(error => {
console.log(error)
})
}
render(){
return(
<div>
<h2> Add New User </h2>
<form className="form-horizontal" onSubmit={this.handleSubmit.bind(this)}>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="email">Name:</label>
<div className="col-sm-10">
<input type="text" className="form-control" id="name" placeholder="Enter name" value={this.state.name}
onChange={ this.handleNameChange.bind(this) }/>
</div>
</div>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="email">Email:</label>
<div className="col-sm-10">
<input type="email" className="form-control" id="email" placeholder="Enter email" value={this.state.email}
onChange={ this.handleEmailChange.bind(this) }/>
</div>
</div>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="email">Password:</label>
<div className="col-sm-10">
<input type="password" className="form-control" id="password" placeholder="Enter password" value={this.state.password}
onChange={ this.handlePasswordChange.bind(this) }/>
</div>
</div>
<div className="form-group">
<button type="submit" className="btn btn-default">Update</button>
</div>
</form>
</div>
)
}
}
if (document.getElementById('edit')) {
var id=$("#edit").data("id");
ReactDom.render(<Edit id={id}/>, document.getElementById('edit'))
}
You are using value attribute to set default value while rendering, it makes your input controlled input, then when updating your states you are trying to change your input values again which will make it uncontrolled one. basically for your code to succeed you need to use defaultValue attribute in your rendered code instead of value attr. because when you are setting value to input from html it can't be later changed.
do this instead for your input elements:
<input type="email" className="form-control" id="email" placeholder="Enter email" defaultValue={this.state.email} />
here is a source from react docs on issue:
https://reactjs.org/docs/uncontrolled-components.html
The error is occuring because when you're doing setState inside componentDidMount, the value returned seems to be undefined.
if(user.name!==undefined && user.email!==undefined){
this.setState({
name: user.name,
email: undefined,
})
}
This will prevent the values in the state from becoming undefined and you won't have that error anymore.

Categories