ANTD - How to change input value when blur another input - reactjs

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
};

Related

How to Use antd upload with Form.Item and Form.List ,finally get uploaded filePath when onFinish?

I'm a beginner in react and antd and I want to upload file such as image or video inside Form which has Form.List and Form.Item.
I want when onFinish the form get the uploaded file path inside values.
It is working when using antd Input or Select but when using antd Upload didn't work, I think I am missing something. When using getValueFromEvent it work but contain File and FileList. I have an api take formdata and return in success the filePath I want this filePath inside values when finish the form. I hope I have explained the problem in a simple way because I will use nested Form.List.
import React, { useState } from "react";
import { Button, Col, Form, Input, Progress, Row, Upload } from "antd";
import { toastError } from "helpers/toasters";
import { customUploadFile } from "Network";
import { MinusCircleOutlined } from "#ant-design/icons";
function TestUploadWithFormItemAndFormList() {
const [filePath, setFilePath] = useState(null);
const [progressPercent, setProgressPercent] = useState(0);
function customUploadImg(options) {
const { file } = options;
const data = new FormData();
data.append("img", file);
// this is an api with axios take formData and return filePath from backEnd it correctly
customUploadFile(
data,
(progress) => {
const { loaded, total } = progress;
let progressData = (loaded / total) * 100;
if (progress) setProgressPercent(Math.round(progressData));
},
(success) => {
console.log("success", success);
setFilePath(success?.data?.filePath);
/*
output {"isSuccess":true,"data":
{"filePath":"public\\images\\1669204672383_car.jpg","fileName":"1669204672383_car.jpg"}}
*/
},
(fail) => {
console.log("fail", fail);
toastError();
}
);
}
const getFile = (event) => {
console.log("event", event);
};
const onFinish = (values) => {
console.log("values", values);
/* output
users:
Array(2)
0:{userName: 'Ali', userImage: undefined}
1:{userName: 'ahmed', userImage: undefined} why undefined??
*/
};
return (
<div>
<Form name="userData" onFinish={onFinish}>
<Form.List name="users">
{(fields, { add, remove }) => (
<>
{fields.map((field, index) => (
<Row gutter={16} key={field.key}>
<Col xs={24} md={8}>
<Form.Item
name={[field.name, "userImage"]}
getValueFromEvent={getFile}
>
<Upload
customRequest={customUploadImg}
showUploadList={false}
>
<div role={"button"} type="primary">
Click me to upload
</div>
</Upload>
{filePath}
<Progress percent={progressPercent} />
</Form.Item>
</Col>
<Col xs={24} md={12}>
<div className="new-actor__name new-category ">
<Form.Item
label="user name"
className=" wd-100 "
name={[field?.name, "userName"]}
rules={[
{
required: true,
message: "Provide user name",
},
]}
>
<Input />
</Form.Item>
</div>
</Col>
<Col xs={24} md={4}>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Col>
</Row>
))}
<Button onClick={() => add()}>Add Another User</Button>
</>
)}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Save
</Button>
</Form.Item>
</Form>
</div>
);
}
export default TestUploadWithFormItemAndFormList;

The Select Dropdown doesn't update back after reload

I created a form where the user inputs data and when the button Save is clicked the data are saved to a database. Everything works fine. You can notice that the country is selected from a memo list and I used a function to save the data rather than a lambda function like the name and the address because I couldn't figure it out. My problem is when I reload the page the country doesn't get back to the form like the name and the address but is stored in the database. The issue is in the code below.
import React, {useState, useEffect , useMemo} from 'react';
import { Button, Row, Col, Form, InputGroup} from 'react-bootstrap';
import obtainFromServer from '../Services/fetchService';
import { useLocalState } from '../util/useLocalStorage';
import countryList from 'react-select-country-list';
import Select from 'react-select';
const TrademarkView = () => {
const [jwt, setJwt] = useLocalState("", "jwt"); //Remove setJwt if we don't use it at the end
const trademarkId = window.location.href.split("/trademarks/")[1];
const [trademark, setTrademark] = useState({
address: "",
country: "",
name: "",
});
//Countries
const [countryinit, setCountryinit] = useState({
value: "",
label: "",
});
const options = useMemo(() => countryList().getData(), []);
const changeHandler = countryinit => {
setCountryinit(countryinit);
console.log(countryinit);
updateTrademark("country",countryinit.label)
}
const styleCountries = {
display: "inline-block",
width: "300px"
};
function updateTrademark(prop, value){
const newTrademark = {...trademark}
newTrademark[prop] = value;
setTrademark(newTrademark);
}
function saveTrademark(){
obtainFromServer(`/api/trademarks/${trademarkId}`, "PUT", jwt, trademark).then(
(trademarkData) =>{
setTrademark(trademarkData);
});
}
useEffect(() => {
obtainFromServer(`/api/trademarks/${trademarkId}`, "GET", jwt)
.then(trademarkData => {
if(trademarkData.address === null) trademarkData.address = "";
if(trademarkData.name === null) trademarkData.name = "";
setTrademark(trademarkData);
});
},[])
return (
<div>
{trademark ? (
<>
<Row>
<Col>
<Form className='m-5'>
<Form.Group controlId="formGridName" className="mb-2">
<Form.Label>Name:</Form.Label>
<Form.Control value={trademark.name} onChange={(e) => updateTrademark("name",e.target.value)}/>
</Form.Group>
<Form.Group className="mb-3" controlId="formGridAddress">
<Form.Label>Address:</Form.Label>
<Form.Control as="textarea" placeholder="1234 Main St" rows={3} value={trademark.address} onChange={(e) => updateTrademark("address",e.target.value)}/>
</Form.Group>
<Form.Group className="mb-3" controlId="formGridCountry">
<Form.Label>Country:</Form.Label>
<div style={styleCountries}><Select options={options} value={countryinit} onChange={changeHandler} /></div>
</Form.Group>
</Form>
</Col>
<Row className='mx-1 px-5'>
<Button onClick={() => saveTrademark()}>Save</Button>
</Row>
</Row>
</>
) : (
<></>
)}
</div>
);
};
export default TrademarkView;
The countryinit has a value(it's the initials of the country) and a label(it's the name of the country). You can see that when I am updating the database I am only sending the label. I have a console.log so you can see the country object.
How can I get the country to be selected after a reload?
I finally figured it out. Changed the select to this and it worked.
<Select options={options} value={options.filter((option) => option.value === trademark.country)} onChange={(e) => updateTrademark("country",e.value)} />
Basically the problem as I mention before is that the country it was an object. And I couldn't send the whole object to the database and I was sending the label which it was wrong. Now I am sending the value to the database and when reading I am redirecting the value from the database to the selected value and setting the label.

Can't type in react input field

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.

onChange event doesn't fire on first input

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.

How to show default value in console in react?

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)
}

Resources