I'm trying to take the inputs using a form and update the state when the checkbox is clicked but for some reason, the state doesn't update. It doesn't throw any error. I searched on google for answers but none of them were plausible.
My code:
class PatientForm extends React.Component{
constructor(){
super();
this.state={
patientName: "",
age: null,
sex: "",
email: "",
phoneNo: null
}
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit = (event) => {
console.log(this.state);
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
alert(this.state.patientName);
console.log(this.state);
}
render(){
return (
<div className="container-fluid">
<Jumbotron>
<h3 className="display-5">Enter Patient info</h3>
<Form>
<Form.Text className="text-muted">
We'll never share your details with anyone else.
</Form.Text>
<Form.Row>
<Col>
<Form.Control name="patientName" type="text" placeholder="Patient name" required/>
</Col>
<Col>
<Form.Control name="age" type="number" placeholder="Patient Age" required/>
</Col>
<Col>
<Form.Control name="sex" type="text" placeholder="Patient Sex" required/>
</Col>
</Form.Row>
<br />
<Form.Group>
<Form.Control name="email" type="email" placeholder="Enter email" required/>
</Form.Group>
<Form.Group>
<Form.Control name="phoneNo" type="number" placeholder="Phone number" required/>
</Form.Group>
<Form.File />
<Form.Label>Patient's Eth Address</Form.Label>
<Form.Text className="text-muted">
################
</Form.Text>
<Form.Group >
<Form.Check
type="checkbox"
label="Tick if the entered info is correct"
onClick = {this.handleSubmit}
/>
</Form.Group>
<Button variant="primary" type="submit" onClick={ () => console.log(this.state)}>
Submit
</Button>
</Form>
<p>{this.state.patientName}</p>
</Jumbotron>
</div>
)
}
}
export default PatientForm;
Here the handleSubmit function is revoked when the checkbox is clicked.
You need to attach the handleSubmit function to onChange event of every <Form.Control /> you want to use to update the state properties.
Read the "Handling Multiple Inputs" documentation page for more insights.
Here is how you should write you <PatientForm /> (I renamed handleSubmit to handleChange and provide new implementation of handleSumbit):
class PatientForm extends React.Component {
constructor() {
super();
this.state = {
patientName: "",
age: null,
sex: "",
email: "",
phoneNo: null
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
// e.g.: send to remote API
event.preventDefault();
console.log("state", this.state);
console.log("data sent to remote API");
}
render() {
return (
<div className="container-fluid">
<Jumbotron>
<h3 className="display-5">Enter Patient info</h3>
<Form>
<Form.Text className="text-muted">
We'll never share your details with anyone else.
</Form.Text>
<Form.Row>
<Col>
<Form.Control
name="patientName"
type="text"
placeholder="Patient name"
onChange={this.handleChange}
required
/>
</Col>
<Col>
<Form.Control
name="age"
type="number"
placeholder="Patient Age"
onChange={this.handleChange}
required
/>
</Col>
<Col>
<Form.Control
name="sex"
type="text"
placeholder="Patient Sex"
onChange={this.handleChange}
required
/>
</Col>
</Form.Row>
<br />
<Form.Group>
<Form.Control
name="email"
type="email"
placeholder="Enter email"
onChange={this.handleChange}
required
/>
</Form.Group>
<Form.Group>
<Form.Control
name="phoneNo"
type="number"
placeholder="Phone number"
onChange={this.handleChange}
required
/>
</Form.Group>
<Form.File />
<Form.Label>Patient's Eth Address</Form.Label>
<Form.Text className="text-muted">################</Form.Text>
<Form.Group>
<Form.Check
type="checkbox"
label="Tick if the entered info is correct"
/>
</Form.Group>
<Button variant="primary" type="submit" onClick={this.handleSubmit}>
Submit
</Button>
</Form>
<p>{this.state.patientName}</p>
</Jumbotron>
</div>
);
}
}
Related
const AddItem = () => {
const [user] = useAuthState(auth);
const navigate = useNavigate();
const handleAddCar = (e) => {
e.preventDefault();
const carObj = {
name: e.target.name.value,
supplier: e.target.supplier.value,
email: user.email,
price: parseInt(e.target.price.value),
quantity: parseInt(e.target.quantity.value),
description: e.target.description.value,
img: e.target.image.value,
sold: parseInt(e.target.sold.value),
};
axios
.post("http://localhost:5000/car", carObj)
.then((response) => {
toast("Successfully Added");
});
e.target.reset();
};
return (
<div>
<Container>
<Row>
<div className="col-md-6 mx-auto">
<h2 className="my-5">Add A Car</h2>
<Form onSubmit={handleAddCar}>
<Form.Group className="mb-3" controlId="formBasicModel">
<Form.Label>Model</Form.Label>
<Form.Control type="text" name="name" required />
</Form.Group>
<Form.Group className="mb-3" controlId="formBasicSupplier">
<Form.Label>Supplier</Form.Label>
<Form.Control type="text" name="supplier" required />
</Form.Group>
<Form.Group className="mb-3" controlId="formBasicEmail">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
value={user.email}
readOnly
disabled
/>
</Form.Group>
<Form.Group className="mb-3" controlId="formBasicPrice">
<Form.Label>Price</Form.Label>
<Form.Control type="number" name="price" required />
</Form.Group>
<Form.Group className="mb-3" controlId="formBasicQuantity">
<Form.Label>Quantity</Form.Label>
<Form.Control type="number" name="quantity" required />
</Form.Group>
<Form.Group className="mb-3" controlId="formBasicDescription">
<Form.Label>Description</Form.Label>
<br />
<textarea
className="w-100"
name="description"
required
></textarea>
</Form.Group>
<Form.Group className="mb-3" controlId="formBasicImage">
<Form.Label>Image</Form.Label>
<Form.Control type="text" name="image" required />
</Form.Group>
<Form.Group className="mb-3" controlId="formBasicImage">
<Form.Label>Sold</Form.Label>
<Form.Control type="number" name="sold" required />
</Form.Group>
<Button variant="primary" type="submit" className="w-100 fw-bold">
Add Car
</Button>
</Form>
</div>
</Row>
</Container>
</div>
);
};
I am trying to add an item in mongodb database by a form where I set some default value and required all input field. It is working properly but getting a warning on console "A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen."
Your problem is here:
<Form.Control
type="email"
value={user.email}
readOnly
disabled
/>
You can see you're setting the value of this component to user.email, making it a controlled input (where the value is passed in, and controlled from a parent component usually via a state).
an uncontrolled component is - basically - one where the value is not passed in, and the component itself deals with the value.
I'd guess that your user object (or the email parameter at least) is undefined to begin with, and then populates via an API call of some kind.
you either need to always have user.email parameter available when this input is rendered (eg. wrap the input in a check so it only renders when user.email !== undefined or add in a placeholder email param when the user object is first initialised), or less ideally supply a value for when the value is undefined. (eg. value={user.email ?? '-'} )
I've got a form that submits data to an API and from there into a MySQL database.
The form has a Form.Check of type switch. And when switched on provides the value on. However, I've set up my database to accept a tinyint - either 1 or 0.
Is there a way to return 1 instead of on? For the console output below, console.log(formData.favourite) and console.log(formData.scheduled) currently return on or nothing at all.
Handler
const initialFormData = Object.freeze({
title: "",
summary: "",
wiki_source: "",
date: "",
scheduled: "",
favourite: "",
location_id: ""
});
const [formData, updateFormData] = React.useState(initialFormData);
const handleChange = (e) => {
updateFormData({
...formData,
// Trimming any whitespace
[e.target.name]: e.target.value.trim()
});
};
const handleSubmit = (e) => {
e.preventDefault()
console.log(formData.title);
console.log(formData.summary);
console.log(formData.wiki_source);
console.log(formData.date);
console.log(formData.scheduled);
console.log(formData.favourite);
console.log(formData.location_id);
}
Modal
<Modal show={isOpen} onHide={closeModal}>
<Modal.Header closeButton>
<Modal.Title>New Event</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group controlId="formTitle">
<Form.Label>Title:</Form.Label>
<Form.Control size="sm" type="text" name="title" placeholder="Title of event" onChange={handleChange}/>
<Form.Text className="text-muted">
The url of the source you are adding
</Form.Text>
</Form.Group>
<Form.Group controlId="formSummary">
<Form.Label>Summary</Form.Label>
<Form.Control size="sm" type="text" name="summary" placeholder="Summary" onChange={handleChange}/>
</Form.Group>
<Form.Group controlId="formWikiSource">
<Form.Label>Wiki Source</Form.Label>
<Form.Control size="sm" type="text" name="wiki_source" placeholder="Wiki Page URL" onChange={handleChange}/>
</Form.Group>
<Form.Group controlId="formDate">
<Form.Label>Date</Form.Label>
<Form.Control size="sm" type="date" name="date" onChange={handleChange}/>
</Form.Group>
<Form.Group controlId="formScheduled">
<Form.Check type="switch" name="scheduled" label="Set as a scheduled event" onChange={handleChange} />
</Form.Group>
<Form.Group controlId="formFavourite">
<Form.Check type="switch" name="favourite" label="Set as favourite" onChange={handleChange} />
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={handleSubmit}>
Submit
</Button>
</Modal.Footer>
</Modal>
I'm going to assume Form.Check is a wrapper for an input type="checkbox". If so, and assuming it supports the usual value property, you can use value="1" to make the checkbox value "1" (a string) rather than "on". It's still a string, though you could convert it to a number, for instance:
updateFormData({
...formData,
// Trimming any whitespace
[e.target.name]: e.target.type === "checkbox" ? +e.target.value : e.target.value.trim()
});
Or just look for "on" directly:
updateFormData({
...formData,
// Trimming any whitespace
[e.target.name]: e.target.type === "checkbox" ? (e.target.value === "on" ? 1 : 0) : e.target.value.trim()
});
import React, { Component } from "react";
import fire from "../config/fire";
import { Form, Button } from "react-bootstrap";
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import logo from "./logo.png";
export default class CreateUser extends React.Component {
constructor(props) {
super(props);
this.login = this.login.bind(this);
this.handleChange = this.handleChange.bind(this);
this.state = {
email: "",
pwd: "",
name: "",
phoneNo: "",
address: "",
};
}
login(e) {
const db = fire.firestore();
e.preventDefault();
fire
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.pwd)
// .signInWithEmailAndPassword(this.state.email, this.state.pwd)
.then((u) => {
// console.log(u.user.uid);
return db.collection("createdUsers").doc(u.user.uid).set({
email: this.state.email,
name: this.state.name,
phoneNo: this.state.phoneNo,
address: this.state.address,
});
})
.then(() => {
this.setState({
email: "",
pwd: "",
name: "",
phoneNo: "",
address: "",
});
})
.then(() => {
alert("Successfully Created the user!");
})
.catch((error) => {
alert(error);
});
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
render() {
return (
<Container style={{ margin: 0, padding: 0, maxWidth: 1366 }}>
<Row>
{/* <Col
style={{
backgroundColor: "#EFEFEF",
paddingTop: 250,
height: 700,
}}
> */}
{/* <img src={logo} alt="Logo" width="150px" /> */}
{/* </Col> */}
<Col
// style={{
// backgroundColor: "#ffffff",
// paddingTop: 250,
// height: 700,
// }}
>
<div>
<Form style={{ width: 300, height: 300, margin: "0 auto" }}>
<Form.Group controlId="formBasicEmail">
<Form.Label>Username</Form.Label>
<Form.Control
type="email"
placeholder="Enter your email"
id="email"
name="email"
placeholder="Enter your email address"
onChange={this.handleChange}
value={this.state.email}
required
/>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
placeholder="Password"
id="pwd"
name="pwd"
placeholder="Password"
onChange={this.handleChange}
value={this.state.pwd}
required
/>
</Form.Group>
<Form.Group controlId="formBasicInput1">
<Form.Label>Name</Form.Label>
<Form.Control
type="text"
placeholder="Name"
id="name"
name="name"
placeholder="Name"
onChange={this.handleChange}
value={this.state.name}
required
/>
</Form.Group>
<Form.Group controlId="formBasicInput2">
<Form.Label>Phone Number</Form.Label>
<Form.Control
type="text"
placeholder="Phone Number"
id="phoneNo"
name="phoneNo"
onChange={this.handleChange}
value={this.state.phoneNo}
required
/>
</Form.Group>
<Form.Group controlId="formBasicInput3">
<Form.Label>Address</Form.Label>
<Form.Control
type="text"
placeholder="Address"
id="address"
name="address"
onChange={this.handleChange}
value={this.state.address}
required
/>
</Form.Group>
<Button variant="primary" type="submit" onClick={this.login}>
Create User
</Button>
</Form>
</div>
</Col>
</Row>
</Container>
);
}
}
In the above code, I'm creating a user and with some custom fields. And I'm already logging into the app from some other component(using signInWithEmailAndPassword) and then inside app, I'm creating users, But, after clicking "create user" button, it's making me log out and if I again log in with the credentials from which I used to log in always, after logging in its showing me the alert message, and then it's creating user and storing the values in firestore collection.
Now in this, It should create user and store the custom values in firestore collection. And then it should make fields empty and then it should show alert message. But it should not make me log out.
Please help me out with this...
I am currently building a simple signup form page to build my knowledge of React Web Apps. I have a number of different types of form elements. I am running into the following error when I use my custom hook to handle changes in any of the form fields that are not simple text fields.
TypeError: Cannot read property 'name' of undefined
(anonymous function)
C:/Users/paris/Desktop/covidsocial/frontend/src/libs/hooksLib.js:11
function(event) {
9 | setValues({
10 | ...fields,
> 11 | [event.target.name]: event.target.value
| ^ 12 | });
13 | }
14 | ];
The above error message occurs anytime i select a country from the region and country selector drop-downs or the date picker. I have tried using both name and control-id elements as the ids but I still get the same error message as above. I think the issue is with how I am setting my values, but I am very new to React and am having trouble pinning down exactly whats going on. Any help would be greatly appreciated!
Here is the relevant code for my Signup.js class as well as the custom react hook
hookslib.js
import { useState } from "react";
export function useFormFields(initialState) {
const [fields, setValues] = useState(initialState);
return [
fields,
function(event) {
setValues({
...fields,
[event.target.name]: event.target.value
});
}
];
}
Signup.js
export default function Signup() {
const [fields, handleFieldChange] = useFormFields({
email: "",
password: "",
confirmPassword: "",
confirmationCode: "",
gender: "",
onList: false,
dateOfBirth: "",
mailingAddressLine1: "",
mailingAddressLine2: "",
mailingAddressCity: "",
mailingAddressState: "",
mailingAddressZip: "",
billingAddressLine1: "",
billingAddressLine2: "",
billingAddressCity: "",
billingAddressState: "",
billingAddressZip: "",
useShippingAsBilling: false,
});
const history = useHistory();
const [newUser, setNewUser] = useState(null);
const { userHasAuthenticated } = useAppContext();
const [isLoading, setIsLoading] = useState(false);
function validateForm() {
return (
fields.email.length > 0 &&
fields.password.length > 0 &&
fields.password === fields.confirmPassword
);
}
function validateConfirmationForm() {
return fields.confirmationCode.length > 0;
}
async function handleSubmit(event) {
event.preventDefault();
setIsLoading(true);
try {
const newUser = await Auth.signUp({
username: fields.email,
password: fields.password,
confirmPassword: fields.confirmPassword,
gender: fields.gender,
firstName: fields.firstName,
lastName: fields.lastName,
onList: fields.onList,
dateOfBirth: fields.dateOfBirth,
mailingAddressLine1: fields.mailingAddressLine1,
mailingAddressLine2: fields.mailingAddressLine2,
mailingAddressCity: fields.mailingAddressCity,
mailingAddressState: fields.mailingAddressState,
mailingAddressZip: fields.mailingAddressZip,
mailingAddressCountry: fields.mailingAddressCountry,
billingAddressCountry: fields.billingAddressCountry,
billingAddressLine1: fields.billingAddressLine1,
billingAddressLine2: fields.billingAddressLine2,
billingAddressCity: fields.billingAddressCity,
billingAddressState: fields.billingAddressState,
billingAddressZip: fields.billingAddressZip,
});
setIsLoading(false);
setNewUser(newUser);
} catch (e) {
onError(e);
setIsLoading(false);
}
}
async function handleConfirmationSubmit(event) {
event.preventDefault();
setIsLoading(true);
try {
await Auth.confirmSignUp(fields.email, fields.confirmationCode);
await Auth.signIn(fields.email, fields.password);
userHasAuthenticated(true);
history.push("/");
} catch (e) {
onError(e);
setIsLoading(false);
}
}
function renderConfirmationForm() {
return (
<form onSubmit={handleConfirmationSubmit}>
<FormGroup controlId="confirmationCode" bsSize="large">
<ControlLabel>Confirmation Code</ControlLabel>
<FormControl
autoFocus
type="tel"
onChange={handleFieldChange}
value={fields.confirmationCode}
/>
<HelpBlock>Please check your email for the code.</HelpBlock>
</FormGroup>
<LoaderButton
block
type="submit"
bsSize="large"
isLoading={isLoading}
disabled={!validateConfirmationForm()}
>
Verify
</LoaderButton>
</form>
);
}
function renderForm() {
return (
<form onSubmit={handleSubmit}>
<FormGroup controlId="email" bsSize="large">
<ControlLabel>Email</ControlLabel>
<FormControl
autoFocus
name = "email"
type="email"
value={fields.email}
onChange={handleFieldChange}
/>
</FormGroup>
<FormGroup controlId="password" bsSize="large">
<ControlLabel>Password</ControlLabel>
<FormControl
type="password"
name = "password"
value={fields.password}
onChange={handleFieldChange}
/>
</FormGroup>
<FormGroup controlId="confirmPassword" bsSize="large">
<ControlLabel>Confirm Password</ControlLabel>
<FormControl
type="password"
name = "confirmPassword"
onChange={handleFieldChange}
value={fields.confirmPassword}
/>
</FormGroup>
<FormGroup controlId="mailingAddressLine1" bsSize="large">
<ControlLabel>Mailing Address Line 1</ControlLabel>
<FormControl
type="text"
name = "mailingAddressLine1"
onChange={handleFieldChange}
value={fields.mailingAddressLine1}
/>
</FormGroup>
<FormGroup controlId="mailingAddressLine2" bsSize="large">
<ControlLabel>Mailing Address Line 2</ControlLabel>
<FormControl
type="text"
name = "mailingAddressLine2"
onChange={handleFieldChange}
value={fields.mailingAddressLine2}
/>
</FormGroup>
<FormGroup controlId="mailingAddressCountry" bsSize="large">
<ControlLabel>Country</ControlLabel>
<CountryDropdown
name = "mailingAddressCountry"
country={fields.mailingAddressCountry}
value={fields.mailingAddressCountry}
onChange={handleFieldChange} />
</FormGroup>
<FormGroup controlId="mailingAddressRegion" bsSize="large">
<ControlLabel>State</ControlLabel>
<RegionDropdown
//country={country}
name="mailingAddressRegion"
country={fields.mailingAddressCountry}
value={fields.mailingAddressState}
onChange={handleFieldChange} />
</FormGroup>
<FormGroup controlId="city" bsSize="large">
<ControlLabel>City</ControlLabel>
<FormControl
type="text"
name="city"
onChange={handleFieldChange}
value={fields.mailingAddressCity}
/>
</FormGroup>
<FormGroup controlId="zipCode" bsSize="large">
<ControlLabel>Zip Code</ControlLabel>
<FormControl
type="text"
name="zipCode"
onChange={handleFieldChange}
value={fields.mailingAddressZip}
/>
</FormGroup>
<FormGroup controlId="useShippingAsBilling" bsSize="large">
<label>
Use Shipping As Billing:
<input
name = "useShippingAsBilling"
type="checkbox"
id="useShippingasBilling"
value={fields.useShippingAsBilling}
checked={fields.useShippingAsBilling === true}
onChange={handleFieldChange} />
</label>
</FormGroup>
<br />
<FormGroup controlId="billingAddressLine1" bsSize="large">
<ControlLabel>Billing Address Line 1</ControlLabel>
<FormControl
name ="billingAddressLine1"
type="text"
value={fields.billingAddressLine1}
onChange={handleFieldChange}
/>
</FormGroup>
<FormGroup controlId="billingAddressLine2" bsSize="large">
<ControlLabel>Billing Address Line 2</ControlLabel>
<FormControl
name ="billingAddressLine2"
type="text"
value={fields.billingAddressLine2}
onChange={handleFieldChange}
/>
</FormGroup>
<FormGroup controlId="billingAddressCountry" bsSize="large">
<ControlLabel>BillingAddressCountry</ControlLabel>
<CountryDropdown
name="billingAddressCountry"
value={fields.billingAddressCountry}
onChange={handleFieldChange}
/>
</FormGroup>
<FormGroup controlId="mailingAddressRegion" bsSize="large">
<ControlLabel>Billing Address Region</ControlLabel>
<RegionDropdown
name="mailingAddressRegion"
country={fields.billingAddressCountry}
value={fields.billingAddressRegion}
onChange={handleFieldChange} />
</FormGroup>
<FormGroup controlId="billingCity" bsSize="large">
<ControlLabel>City</ControlLabel>
<FormControl
name = "billingCity"
type="text"
value={fields.billingAddressCity}
onChange={handleFieldChange}
/>
</FormGroup>
<FormGroup controlId="billingZipCode" bsSize="large">
<ControlLabel>Zip Code</ControlLabel>
<FormControl
name = "billingZipCode"
type="text"
value={fields.billingAddressZip}
onChange={handleFieldChange}
/>
</FormGroup>
<FormGroup controlId="gender" bsSize="sm">
<ControlLabel>Gender</ControlLabel>
<br></br>
<ControlLabel>Male</ControlLabel>
<FormControl
name = "gender"
type="radio"
onChange={handleFieldChange}
value={fields.gender}
checked={fields.gender === 'male'}
/>
<ControlLabel>Female</ControlLabel>
<FormControl
name = "gender"
type="radio"
onChange={handleFieldChange}
value={fields.gender}
checked={fields.gender === 'female'}
/>
<ControlLabel>Other</ControlLabel>
<FormControl
name = "gender"
type="radio"
onChange={handleFieldChange}
value={fields.gender}
checked={fields.gender === 'other'}
/>
</FormGroup>
<FormGroup controlId="onList" bsSize="sm">
<br></br>
<ControlLabel>Join Email List</ControlLabel>
<br></br>
<ControlLabel>Yes</ControlLabel>
<FormControl
name = "onList"
type="checkbox"
onChange={handleFieldChange}
value={fields.onList}
checked={fields.onList === 'true'}
/>
</FormGroup>
<FormGroup controlId="dateOfBirth">
<ControlLabel>Date of Birth</ControlLabel>
<DatePicker
name="dateOfBirth"
value={fields.dateofBirth}
selected={fields.dateOfBirth}
onChange={handleFieldChange}
locale="en"
/>
</FormGroup>
<FormGroup controlId="content">
<ControlLabel>Notes Area</ControlLabel>
<FormControl
name = "content"
value={fields.content}
componentClass="textarea"
onChange={handleFieldChange}
/>
</FormGroup>
<LoaderButton
block
type="submit"
bsSize="large"
isLoading={isLoading}
disabled={!validateForm()}
>
Signup
</LoaderButton>
</form>
);
}
return (
<div className="Signup">
{newUser === null ? renderForm() : renderConfirmationForm()}
</div>
);
}
This is a clever approach to handling change events and persisting values to state. You do, however, put a lot of faith in this component library to fire onChange events consistently with their HTML counterparts.
I noticed in the docs that the onChange is a little different than you might expect:
<CountryDropdown
value={country}
onChange={(val) => this.selectCountry(val)} />
<RegionDropdown
country={country}
value={region}
onChange={(val) => this.selectRegion(val)} />
Both of these expect an arrow function handler to receive the val property, so you might consider rewriting your implementation:
<CountryDropdown
name = "mailingAddressCountry"
country={fields.mailingAddressCountry}
value={fields.mailingAddressCountry}
onChange={(val) => updateField({ mailingAddress: val})} />
And modify your hook to so that you aren't working from the bare event. It'll require a little more syntax, and there's probably a cleaner way to approach, but it will give you the control you need to handle edge cases. Something like so:
import { useState } from "react";
export function useFormFields(initialState) {
const [fields, setValues] = useState(initialState);
return [
fields,
function(value) {
setValues({
...fields,
...value
});
}
];
}
Here's my Content wrapper which will either display the SignUp component or the SignIn component.
const Content = (props) => {
return (
<div className="wrapper">
<SignIn/>
<SignUp/>
</div>
)
};
export default Content;
Here's my SignUp component
class SignUp extends React.Component {
state = {
email: '',
username: '',
password: '',
second_password: '',
showModal: false,
modalMessage: '',
modalHeader: ''
};
handleSubmit = async (event) => {
event.preventDefault();
const emailValidated = await this.validateEmail(this.state.email);
const usernameValidated = this.validateUsername(this.state.username);
const passwordValidated = this.validatePassword(this.state.password, this.state.second_password);
if (emailValidated === true){
if(usernameValidated === true){
if(passwordValidated === true){
const registrationComplete = await this.register(this.state.email, this.state.username, this.state.password);
if (registrationComplete === true) {
this.setState(prevState => ({
...prevState,
showModal: true,
modalMessage: "Please check your mail to activate your account",
modalHeader: "Success!"
}));
}else{
this.setState(prevState => ({
...prevState,
showModal: true,
modalMessage: "Something went wrong with the registration. Please try again later.",
modalHeader: "Failure!"
}));
}
}else{
this.setState(prevState => ({
...prevState,
showModal: true,
modalMessage: "Passwords do not match",
modalHeader: "Failure!"
}));
}
}else{
this.setState(prevState => ({
...prevState,
showModal: true,
modalMessage: "Username should be a minimum of 8 characters long",
modalHeader: "Failure!"
}));
}
}else{
this.setState(prevState => ({
...prevState,
showModal: true,
modalMessage: "An account with that email already exists.",
modalHeader: "Failure!"
}));
}
};
validateUsername = (username) => {
return username.trim().length >= 8;
};
validatePassword = (password, second) => {
return password.trim() === second.trim();
};
validateEmail = async (email) => {
const resp = await axios({
method: 'post',
url: 'bouncr/user/exists',
data: {"email": email}
});
return resp.status === 200
};
register = async (email, username, password) => {
const resp = await axios({
method: 'post',
url: 'bouncr/user/register',
data: {'email': email, 'username': username, 'password': password}
});
return resp.status === 201
};
onClose = () => {
this.setState(prevState => ({...prevState, showModal: false, modalMessage: '', modalHeader: ''}));
};
render() {
return (
<div>
<CustomModal
showModal={this.state.showModal}
onClose={this.onClose}
modalMessage={this.state.modalMessage}
modalHeader={this.state.modalHeader}
/>
<div className="register">
<h1>Sign Up</h1>
<Form onSubmit={this.handleSubmit}>
<Form.Group controlId="email">
<Form.Label>Email address</Form.Label>
<Form.Control type="email"
placeholder="Enter email"
value={this.state.email}
onChange={event => this.setState({email: event.target.value})}/>
<Form.Text className="text-muted">
Please make sure you've access to this mail. You'll receive an activation code here.
</Form.Text>
</Form.Group>
<Form.Group controlId="username">
<Form.Label className="form-label">Username</Form.Label>
<Form.Control type="text"
placeholder="Username"
value={this.state.username}
onChange={event => this.setState({username: event.target.value})}/>
<Form.Text className="text-muted">
Please make it atleast 8 characters long.
</Form.Text>
</Form.Group>
<Form.Group controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control type="password"
placeholder="Password"
value={this.state.password}
onChange={event => this.setState({password: event.target.value})}/>
<Form.Text className="text-muted">
Please make sure it's atleast 8 characters long and uses a mix of letters and numbers.
</Form.Text>
</Form.Group>
<Form.Group controlId="confirmPassword">
<Form.Label>Confirm Password</Form.Label>
<Form.Control type="password"
placeholder="Password"
value={this.state.second_password}
onChange={event => this.setState({second_password: event.target.value})}/>
</Form.Group>
<Button variant="primary" type="submit">
Sign Up
</Button>
<hr/>
<p onClick={() => this.props.setIsNew(false)}>
If you don't have an account, Sign Up
</p>
</Form>
</div>
</div>
);
}
}
export default SignUp;
And here's the SignIn component.
const SignIn = () => {
return (
<div className="sign-up">
<h1>Sign In</h1>
<Form>
<Form.Group controlId="email">
<Form.Label>Email address</Form.Label>
<Form.Control type="email" placeholder="Enter email"/>
<Form.Text className="text-muted">
</Form.Text>
</Form.Group>
<Form.Group controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control type="password" placeholder="Password"/>
</Form.Group>
<Form.Group controlId="formBasicCheckbox">
<Form.Check type="checkbox" label="Remember me"/>
</Form.Group>
<Button variant="primary" type="submit">
Sign In
</Button>
<hr/>
<p>If you don't have an account, Sign Up</p>
</Form>
</div>
)
};
export default SignIn;
I want the text underneath the button to route to the relevant component. How do I do this with React router?
Manage state in parent component. This is a basic example using hooks. This component will determine which child component is rendered based on it's state.
const Content = props => {
const [isNew, setIsNew] = useState(true);
return (
<div className="wrapper">
{isNew ? <SignUp setIsNew={setIsNew} /> : <SignIn setIsNew={setIsNew} />}
</div>
);
};
export default Content;
Pass setIsNew to children and call with proper boolean values. We pass this function and bind it to an onClick handler.
const SignIn = ({setIsNew}) => {
return (
<div className="sign-up">
<h1>Sign In</h1>
<Form>
<Form.Group controlId="email">
<Form.Label>Email address</Form.Label>
<Form.Control type="email" placeholder="Enter email" />
<Form.Text className="text-muted"></Form.Text>
</Form.Group>
<Form.Group controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control type="password" placeholder="Password" />
</Form.Group>
<Form.Group controlId="formBasicCheckbox">
<Form.Check type="checkbox" label="Remember me" />
</Form.Group>
<Button variant="primary" type="submit">
Sign In
</Button>
<hr />
<p onClick={() => setIsNew(true)}>
If you don't have an account, Sign Up
</p>
</Form>
</div>
);
};
export default SignIn;
Same thing as SignIn.
const SignUp = ({setIsNew}) => {
return (
<div className="sign-up">
<h1>Sign In</h1>
<Form>
<Form.Group controlId="email">
<Form.Label>Email address</Form.Label>
<Form.Control type="email" placeholder="Enter email" />
<Form.Text className="text-muted"></Form.Text>
</Form.Group>
<Form.Group controlId="username">
<Form.Label>Email address</Form.Label>
<Form.Control type="email" placeholder="Username" />
<Form.Text className="text-muted"></Form.Text>
</Form.Group>
<Form.Group controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control type="password" placeholder="Password" />
</Form.Group>
<Form.Group controlId="confirmPassword">
<Form.Label>Password</Form.Label>
<Form.Control type="password" placeholder="Confirm Password" />
</Form.Group>
<Button variant="primary" type="submit">
Sign In
</Button>
<hr />
<p onClick={() => setIsNew(false)}>
If you don't have an account, Sign Up
</p>
</Form>
</div>
);
};
export default SignUp;