I am working on a React project, In that Project I have a form, In that form I have two Input
Tags. For first input tag I defined the value, For the second input tag I am assigning value by
using state. Now I am trying to console values of two Input tags but I am getting only second
Input tag value. So someone please help me to get Two Input tag values from form
This is Form.js
import React, { useState } from 'react';
import './aum-company-modal.css';
import Aumservice from '../../service/aum-service';
import { Button, Col, Modal, ModalBody,ModalFooter, ModalHeader, Row, FormGroup, Label, Input, } from 'reactstrap';
const AumCompanyModal = (props) => {
const [data, sendData] = useState({})
const [firstInputValue] = useState('1000')
const [inputValue, setInputValue] = useState('');
const handleChange = ({target}) => {
const { name, value } = target;
console.warn(name,value)
const newData = Object.assign({}, data, { [name]: value })
sendData(newData)
if (value > -1 && value < 100000000000000000000000000000000000) {
setInputValue(value);
}
}
const handleSubmit = (e) => {
e.preventDefault()
console.log(data)
}
return (
<Row>
<Col md="6" sm="6" xs="6">
<Modal isOpen={props.openModal}
>
<ModalHeader >Add new</ModalHeader>
<ModalBody>
<Row>
<Col md="12" sm="12" xs="12">
<FormGroup>
<Label for="exampleName">Min Value</Label>
<Input
type="text"
name="minValue"
placeholder="Enter minimum value"
value={firstInputValue}
/>
</FormGroup>
<FormGroup>
<Label for="exampleName">Max Value</Label>
<Input
type="number"
name="maxValue"
placeholder="Enter maximum value"
min='1000' max='100000000000000000000000000000000000'
value={inputValue}
onChange={handleChange}
/>
</FormGroup>
</Col>
</Row>
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={props.closeModal}>
Cancel
</Button>
<Button type="submit" onClick={handleSubmit} color="primary">
Submit
</Button>
</ModalFooter>
</Modal>
</Col>
</Row>
)
}
export default AumCompanyModal
If you have any doubt please put a comment.
If for some reason, you need to hard code some value, use state variable as value and then you will have that variable in handleSubmit. Or you can use ref and get value from that ref
const [firstValue] = useState('100');
<Input
type="text"
name="minValue"
placeholder="Enter minimum value"
value={firstValue}
/>
Or you can use React.useRef()
const firstInput = React.useRef();
<Input
type="text"
name="minValue"
placeholder="Enter minimum value"
value="1000"
ref={firstInput}
/>
// in handleSubmit
const handleSubmit = (e) => {
e.preventDefault()
console.log(firstInput.current.value)
}
Related
I have a simple form with an input field that I can't type on. I first thought the problem was with the onChange or the value props that were setting the input to readonly, but the fact is that I cant type with the browser suggestions and the state updates perfectly (See gif here) it's just that I won't let me type with the keyboard, even after reloading the page.
I also have a Login page that works perfectly except when I log out and redirect back to that page, it won't work until I reload the page, now it will work.
<input
value={name}
onChange={handleChange}
name="name"
/>
const [name, setName] = useState("");
const handleChange = (e:any) => {
setName(e.target.value);
}
Weird thing is that it's in like a readonly state but when I use browser suggestions it works and updates the state.
Here is the whole component:
import React, { useEffect, useState } from 'react';
import { useForm } from '../../utils/useForm';
import { CubeType } from '../../interfaces';
//import useStore from '../store/Store';
import { Modal, Button, Row, Col, FormGroup, FormLabel, FormControl } from 'react-bootstrap';
type Props = {
show: Boolean,
onClose: () => void,
cubeTypes: CubeType[]
};
const ModalTimelist = (props: Props) => {
//const store = useStore();
const [values, handleChangee] = useForm({ cubeType: 1, name: '' });
const [name, setName] = useState("");
const handleChange = (e:any) => {
setName(e.target.value);
}
useEffect(() => {
const modal = document.getElementsByClassName('modal')[0];
if(modal) modal.removeAttribute('tabindex');
}, [props.show]);
return (
<>
<Modal show={props.show} onHide={ props.onClose }>
<Modal.Header>
<Modal.Title>Timelist { name }</Modal.Title>
</Modal.Header>
<Modal.Body>
<Row>
<Col md="3">
<FormGroup>
<FormLabel>Cube Type</FormLabel>
<select
value={values.cubeType}
onChange={ handleChangee }
className="form-select"
name="cubeType"
>
{props.cubeTypes.map((it, idx) => {
return (<option value={ idx } key={"cube"+idx}>{it.name}</option>);
}) }
</select>
</FormGroup>
</Col>
<Col md="9">
<FormGroup>
<FormLabel>Name</FormLabel>
<FormControl
value={name}
onChange={handleChange}
name="name"
/>
</FormGroup>
</Col>
</Row>
</Modal.Body>
<Modal.Footer>
<Button variant="success" onClick={() => props.onClose()}>
Save
</Button>
<Button variant="outline-danger" onClick={() => props.onClose()}>
Cancel
</Button>
</Modal.Footer>
</Modal>
</>
);
}
export default ModalTimelist;
value of input must be the state value otherwise it will not change use this code
const App = () => {
const [name,setName] = useState("")
const handle = ({target:{value}}) => setName(value)
return <input
value={name}
onChange={handle}
name="name"
/>
}
Use a debounce for setting name on state.
Example:
const handleChange = (e:any) => {
debounce(() => { setName(e.target.value) }, 300);
}
I tried the code and it works fine I think you should change the browser
and if you want
change this
const ModalTimelist = (props: Props) => {
with
const ModalTimelist:React.FC<Props> = (props) => {
Names specified by you in input field attributes must be same as useState names. Otherwise this problem occurs.
Example:
<input type={"text"} className="form-control" placeholder='Enter your User Name' name="username" value={username} onChange={(e)=>onInputChange(e)}/>
In name="username" , username spell must be same as the spell you used in State.
I have a straight forward react form below which called a handlesubmit function when the submit button is clicked. However, it seems that there are two issues. Firstly, the form doesn't validate inputs, and in fact accepts the empty inputs when submitted. Any help is appreciated please.
import Form from "react-bootstrap/Form"
import Button from "react-bootstrap/Button"
import Col from "react-bootstrap/Col"
import { useState } from "react"
const Register = () => {
const [validated, setValidated] = useState(false);
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const handleSubmit = (event) => {
const form = event.currentTarget;
console.log("in handlesubmit: " + form.checkValidity())
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
setValidated(true);
console.log("in handlesubmit: " + validated)
};
console.log('First Name: ' + firstName);
console.log('Last Name: ' + lastName);
return (
<div>
<h1>Registration page</h1>
<Form noValidated validated={validated} onSubmit={handleSubmit} style={{textAlign:'left'}}>
<Form.Row>
<Form.Group as={Col} controlId="formGridFirstName">
<Form.Label>First name</Form.Label>
<Form.Control type="text" placeholder="Enter first name" value={firstName} onChange={(e) => setFirstName(e.target.value)}/>
</Form.Group>
<Form.Group as={Col} controlId="formGridLastName">
<Form.Label>Last name</Form.Label>
<Form.Control type="text" placeholder="Enter last name" value={lastName} onChange={(e) => setLastName(e.target.value)}/>
</Form.Group>
</Form.Row>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
</div>
)
}
export default Register
I think you probably need to add an onclick to your button. Try onClick={()=>{}} and see if that's triggers your form onsubmit?
Link to code sandbox https://stackblitz.com/edit/react-cdv6mx?devtoolsheight=33&file=src/ResetPassword.js
EXPLANATION: When the user enters incorrect details, the server sends 400: bad request error with a message which can be seen in the Response tab under the network's tab. The intention is to validate the form fields before submitting the form and display an error message(s) if incorrect fields are entered. Also, the intention is to disable to reset the button until the form fields match the criteria. So, that will prevent the user from accidentally submitting half-entered fields.
I have a functional component in which I have a simple form.
PROBLEM: I am calling validatForm() method on onChange. The method should first check that the newPassword and confirmPassword are the same and are as per the password rules(requirements) and if true, only then, send the data.
UPDATED CODE IS IN THE STACKBLITZ LINK ABOVE.
I am use useFormInput() like below
const email = useFormInput("");
const newPassword = useFormInput("");
const confirmPassword = useFormInput("");
and I have written a useFormInput() method
const useFormInput = (initialValue) => {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
return {
value,
onChange: handleChange,
};
};
and a validateForm() method and I am passing it my <button/>
/* Your password must contain at least one capital letter, one
*number and one lowercase letter, and it must contain at least 8
*characters*/
const validateForm = (event) => {
let pass = event.target.value;
let reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/;
let test = reg.test(pass);
if (test) {
this.setState({ value: event.target.value });
} else {
alert("password validation unsuccessful. Please try again.");
return;
}
};
and in render()
<FormGroup row>
<Label for="Email" sm={2}>
Email
</Label>
<Col sm={4}>
<Input
type="email"
name="email"
id="Email"
{...email}
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="newPassword" sm={2}>
New Password
</Label>
<Col sm={4}>
<Input
type="password"
name="password"
id="newPassword"
{...newPassword}
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="confirmPassword" sm={2}>
Confirm Password
</Label>
<Col sm={4}>
<Input
type="password"
name="confirmPassword"
id="confirmPassword"
{...confirmPassword}
/>
</Col>
</FormGroup>
<div className="form-actions">
{error && (
<>
<small style={{ color: "red" }}>{error}</small>
<br />
</>
)}
<br />
<Col lg={{ offset: 2 }} sm={{ size: 1 }}>
<Button
className="mail-reset-btn"
block
type="submit"
value={loading ? "Loading..." : "Login"}
onClick={handleReset}
disabled={!validateForm}
>
Reset
</Button>
</Col>
</div>
</Form>
Update your validateForm to destructure the password fields from the form onSubmit event. Create an errors object to track what field errors there are. If the errors object is empty then validation passes, otherwise set error state.
const validateForm = event => {
event.preventDefault();
const { confirmPassword, newPassword } = event.target;
const errors = {};
const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/;
if (!regex.test(newPassword.value)) {
errors.requirementUnmet = "New password does not meet requirements.";
}
if (newPassword.value !== confirmPassword.value) {
errors.passwordMismatch = "Entered passwords do not match.";
}
if (!Object.keys(errors).length) {
alert("password validation successful.");
setError(null);
return;
}
setError(errors);
};
Attach validateForm to the form's onSubmit handle.
<Form onSubmit={validateForm}>
Ensure you've a type="submit" button in the form.
<Button
className="mail-submit-btn"
block
type="submit"
value={loading ? "Loading..." : "Login"}
>
Submit
</Button>
Updated stackblitz (though I've not an account so copying updated code below.)
Note: I simply JSON.stringify(error) the error for simplicity, but you'll want to render this more gracefully.
Entire ResetPassword.js
import React, { useState } from "react";
import {
Button,
Form,
FormGroup,
Input,
Col,
Label,
Modal,
ModalHeader,
ModalBody,
ModalFooter
} from "reactstrap";
const useFormInput = initialValue => {
const [value, setValue] = useState(initialValue);
const handleChange = e => {
setValue(e.target.value);
};
return {
value,
onChange: handleChange
};
};
const ResetPassword = props => {
// form inputs
const email = useFormInput("");
const newPassword = useFormInput("");
const confirmPassword = useFormInput("");
// using hooks
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
const validateForm = event => {
event.preventDefault();
const { confirmPassword, newPassword } = event.target;
const errors = {};
const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/;
if (!regex.test(newPassword.value)) {
errors.requirementUnmet = "New password does not meet requirements.";
}
if (newPassword.value !== confirmPassword.value) {
errors.passwordMismatch = "Entered passwords do not match.";
}
if (!Object.keys(errors).length) {
alert("password validation successful.");
setError(null);
return;
}
setError(errors);
};
const handleReset = e => {
e.preventDefault();
setError(null);
setLoading(true);
// some unrelated redux code
};
return (
<div className="mail-reset" id="forgotPassword">
<div className="mail-reset-content">
<Form onSubmit={validateForm}>
<h3 className="form-title">Enter Information</h3>
<FormGroup row>
<Label for="Email" sm={2}>
Email
</Label>
<Col sm={4}>
<Input
type="email"
name="email"
id="Email"
placeholder="Email"
aria-label="email address"
aria-describedby="email address"
aria-invalid="false"
{...email}
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="newPassword" sm={2}>
New Password
</Label>
<Col sm={4}>
<Input
type="password"
name="password"
id="newPassword"
placeholder="New Password"
aria-label="new password"
aria-describedby="new password"
aria-invalid="false"
{...newPassword}
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="confirmPassword" sm={2}>
Confirm Password
</Label>
<Col sm={4}>
<Input
type="password"
name="confirmPassword"
id="confirmPassword"
placeholder="Confirm Password"
aria-label="new password"
aria-describedby="new password"
aria-invalid="false"
{...confirmPassword}
/>
</Col>
</FormGroup>
<div className="modal-wrapper">
<Col sm={{ size: 4, offset: 2 }}>
<Button onClick={toggle} className="passwordReqBtn">
Password Requirements
</Button>
</Col>
<Modal isOpen={modal} toggle={toggle} className="mail-reset-modal">
<ModalHeader toggle={toggle}>Password Requirements</ModalHeader>
<ModalBody>
Your password must contain at least one capital letter, one
number and one lowercase letter, and it must contain at least 8
characters
</ModalBody>
<ModalFooter>
<Button onClick={toggle} className="btn-modal pull-right">
OK
</Button>{" "}
</ModalFooter>
</Modal>
</div>
<div className="form-actions">
{error && (
<>
<small style={{ color: "red" }}>{JSON.stringify(error)}</small>
<br />
</>
)}
<br />
<Col lg={{ offset: 2 }} sm={{ size: 1 }}>
<Button
className="mail-reset-btn"
block
type="submit"
value={loading ? "Loading..." : "Login"}
>
Submit
</Button>
<Button
className="mail-reset-btn"
block
type="reset"
value={loading ? "Loading..." : "Login"}
onClick={handleReset}
>
Reset
</Button>
</Col>
</div>
</Form>
</div>
</div>
);
};
export default ResetPassword;
I'm new to React and I've got this registration (parent component) where it has an inventory (child component) and I'm changing the state of the inventory once the user inputs the quantity of the item that they hold so I can register them
But onChange seems to be delayed, I input 4 water bottles for example, and it console logs me the default value, only when I input the amount for another item like food it displays me the 4 water bottles and 0 food :(
This is what I'm working with...
Child component:
import React from "react";
import { Col, Row, FormGroup, Label, Input } from "reactstrap";
import waterIcon from "./../assets/water.png";
import soupIcon from "./../assets/food.png";
import medsIcon from "./../assets/aid.png";
import weaponIcon from "./../assets/gun.png";
function Inventory({ onSubmitInventory, currentInventory }) {
const onChangeWater = (e) => {
const newInventory = { ...currentInventory, water: e.target.value };
onSubmitInventory(newInventory);
};
const onChangeSoup = (e) => {
const newInventory = { ...currentInventory, soup: e.target.value };
onSubmitInventory(newInventory);
};
const onChangeMeds = (e) => {
const newInventory = { ...currentInventory, meds: e.target.value };
onSubmitInventory(newInventory);
};
const onChangeWeapon = (e) => {
const newInventory = { ...currentInventory, weapon: e.target.value };
onSubmitInventory(newInventory);
};
return (
<FormGroup>
<Row className="justify-content-center text-center border-top pt-3">
<Col xs="3">
<img src={waterIcon} alt="Water" />
<Label for="inventoryWater">Fiji Water:</Label>
<Input
type="number"
name="inventoryWater"
id="inventoryWater"
placeholder="Enter amount..."
onChange={onChangeWater}
/>
</Col>
<Col xs="3">
<img src={soupIcon} alt="Soup" />
<Label for="inventorySoup">Campbell Soup:</Label>
<Input
type="number"
name="inventorySoup"
id="inventorySoup"
placeholder="Enter amount..."
onChange={onChangeSoup}
/>
</Col>
<Col xs="3">
<img src={medsIcon} alt="Aid" />
<Label for="inventoryMeds">First Aid Pouch:</Label>
<Input
type="number"
name="inventoryMeds"
id="inventoryMeds"
placeholder="Enter amount..."
onChange={onChangeMeds}
/>
</Col>
<Col xs="3">
<img
className="d-block"
style={{ margin: "0 auto" }}
src={weaponIcon}
alt="Gun"
/>
<Label for="inventoryWeapon">AK47:</Label>
<Input
type="number"
name="inventoryWeapon"
id="inventoryWeapon"
placeholder="Enter amount..."
onChange={onChangeWeapon}
/>
</Col>
</Row>
</FormGroup>
);
}
export default Inventory;
Parent component:
import React, { useState } from "react";
import { Col, Row, Button, Form, Label, Input } from "reactstrap";
import { useForm } from "react-hook-form";
import Inventory from "./Inventory";
import MapContainer from "./MapContainer";
function Register() {
const [lonlat, setLonlat] = useState("");
const onMarkerChange = (lonlat) => {
setLonlat(lonlat);
};
const [inventory, setInventory] = useState({
water: 0,
soup: 0,
meds: 0,
weapon: 0,
});
const onSubmitInventory = (newInventory) => {
setInventory(newInventory);
console.log(inventory);
};
const { register, handleSubmit, errors } = useForm();
const onSubmit = (data) => {
console.log(inventory);
const requestOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: data.personName }),
};
console.log(data);
};
return (
<Form
id="registerForm"
name="registerForm"
onSubmit={handleSubmit(onSubmit)}
>
<h4>New Person</h4>
<Row className="border-top pt-4">
<Col xs="8">
<Label for="personName">Your name:</Label>
<Input
className="gray-input"
type="text"
name="name"
id="personName"
placeholder="Enter your name here..."
innerRef={register}
/>
</Col>
<Col xs="2" className="text-center">
<Label for="personAge">Age:</Label>
<Input
className="gray-input"
type="number"
name="age"
id="personAge"
placeholder="Enter age..."
innerRef={register}
/>
</Col>
<Col xs="2" className="text-center">
<Label for="personGender">Gender:</Label>
<Input
type="select"
name="gender"
id="personGender"
innerRef={register}
>
<option defaultValue disabled>
-
</option>
<option>F</option>
<option>M</option>
</Input>
</Col>
</Row>
<Row>
<Col xs="12">
<Input
hidden
id="personLatLon"
name="personLatLon"
type="text"
defaultValue={lonlat}
innerRef={register}
/>
<MapContainer onMarkerChange={onMarkerChange} />
</Col>
</Row>
<h4>Inventory</h4>
<Inventory
onSubmitInventory={onSubmitInventory}
currentInventory={inventory}
/>
<Button
outline
color="secondary"
className="mt-2"
type="submit"
form="registerForm"
>
Submit
</Button>
</Form>
);
}
export default Register;
EDIT: Updated code with tips from answers, still facing the same problem :(
The only problem I see with your code is that you are trying to pass a second argument to setInventory. I don't believe this works with hooks like it did with class components. When I attempt to type that in typescript it throws an instant error.
Just pass the actual data you are trying to send and then call onSubmitInventory(inventory) for example:
const onChangeWeapon = (e) => {
const newInventory = {...inventory, weapon: e.target.value};
setInventory(newInventory);
onSubmitInventory(newInventory);
};
If you have to wait for the next render to call onSubmitInventory, it should be a useEffect in the parent. My next question would be why the parent needs the state and the child also has it as a state? Possibly it should be lifted up. But I'm not sure of that without seeing the parent.
Edit: Is your problem just with the console.log? If you run
const [state, setState] = useState(true);
// ...
setState(false);
console.log(state); // outputs true.
The value of state does not change until the next render. Calling setState will cause a new render, but until that happens, the old value is there. Add a console.log of state in the parent just in the body like here
const [inventory, setInventory] = useState({
water: 0,
soup: 0,
meds: 0,
weapon: 0,
});
console.log('I just rendered, inventory: ', inventory);
You'll see it updates!
The fact that the value of state does not change can bite you in this case:
const [state, setState] = useState(true); // closes over this state!
// ...
setState(!state); // closes over the state value above
setState(!state); // closes over the same state value above
// state will be false on next render
So I use this form if there is any possibility I call setState twice before a render:
const [state, setState] = useState(true);
// ...
setState(state => !state);
setState(state => !state);
// state will be true on next render
Inside your parent component's onSubmitInventory you have named the argument inventory but that already exists in the parent's scope. I suggest renaming that newInventory to be clear about which you are referencing.
Also, it seems like you are keeping track of inventory in both the parent and child's state. Keep it in the parent and pass it down to the child as a prop/props.
I'm using ANTD 4.1.2V and I have an input that receives a Zip code number within a form
When onblur event happens, I call a API passing the Zip code value and it returns me the address info.
What I need to do is to fill the others inputs with that values when onBlur event is triggered in Zip code input.
import React, { useState } from 'react';
import { Form, Input, Row, Col } from 'antd';
import MaskedInput from 'antd-mask-input';
import zipCodeAPI from '../../services/zipCodeAPI';
const ClientForm = () => {
const [loadingCep, setLoadingZipCode] = useState(false);
const [, setZipCode] = useState('');
const onChangePfCep = (value) => setZipCode(value);
const handleZipCode = async (cep) => {
setLoadingZipCode(true);
const { data } = await zipCodeAPI(cep);
const { street } = data;
setPublicPlace(street);
setLoadingZipCode(false);
};
return (
<Form
name="client-form"
onFinish={() => console.log('Test')}
layout="vertical"
initialValues={{ ...initialValues, layout: 'vertical' }}
>
<Row gutter={16} justify="space-between">
<Col span={3}>
<Form.Item label="ZIP CODE" name="zip_code">
<MaskedInput
mask="11111-111"
size="8"
onChange={({ target }) => onChangePfCep(target.value)}
// When onBlur here
onBlur={() => handleZipCode('04223-000')}
placeholder="XXXXX-XXX"
/>
</Form.Item>
</Col>
<Col span={7}>
<Form.Item label="Logradouro" name="street">
{/* Fill this input with the zipcodeAPI payload */}
<Input disabled />
</Form.Item>
</Col>
<Col span={2}>
<Form.Item label="Number" name="number">
{/* Fill this input with the zipcodeAPI payload */}
<Input />
</Form.Item>
</Col>
<Col span={5}>
<Form.Item label="Neighborhood" name="neighborhood">
{/* Fill this input with the zipcodeAPI payload */}
<Input disabled />
</Form.Item>
</Col>
</Row>
</Form>
);
};
export default ClientForm;
Does anybody know how to solve this?
You can do it like this
// you need to get the form instance from the useForm hook
const form = useForm();
const handleZipCode = async (cep) => {
setLoadingZipCode(true);
const { data } = await zipCodeAPI(cep);
const { street, number, neighborhood } = data; // changed
setPublicPlace(street);
setLoadingZipCode(false);
form.setFieldsValue({neighborhood, number, street}) // changed
};