ReactJs: Get data from parent URL and pass it to child component - javascript

I am sending a form data from a form in reactjs. Some pre inputs from the user have to be sent altogether with the form. I get that data from the URL of the parent file and the form is in the child component.
Parent url:
http://localhost:3000/uploadlist?phmcy=2
I have to get the phmcy value from the URL. (Here the data have to be passed is '2').
parent component:
import Upload froimportm './Upload'
import axios from "axios";
import { Link, withRouter } from "react-router-dom";
import { useLocation } from 'react-router';
export default function Uploadlist() {
let phmcy = (new URLSearchParams(window.location.search)).get("phmcy")
var myphmcy = JSON.stringify(phmcy);
//const phmcyvalue = new URLSearchParams(this.props.location.search);
//const phmcy = phmcyvalue.get('phmcy')
console.log(myphmcy);
const ordersAPI= (url='https://localhost:44357/api/Orders') => {
return {
fetchAll: () => axios.get(url),
create: newRecord => axios.post(url, newRecord),
update: (id, updateRecord) => axios.put(url + id, updateRecord),
delete: id => axios.delete(url+id)
}
}
const addOrEdit = (formData, onSuccess) => {
ordersAPI().create(formData)
.then(res => {
onSuccess();
})
.catch(err => console.log(err.response.data))
}
return (
<div className="row">
<div className="jumbotron jumbotron-fluid py-4 "></div>
<div className="container text">
<h1 className="display-4"> Order Register</h1>
</div>
<div className="col-md-4 offset-3">
<Upload
addOrEdit = {addOrEdit}
myphmcy = {myphmcy}
/>
</div>
<div className="col-md-1">
<div> </div>
</div>
</div>
)
}
Child component: (This is where the form is. I have only included a part)
import React, {useState, useEffect} from 'react';
import myphmcy from './Uploadlist';
//const myphmcy = JSON.stringify(phmcy);
console.log(myphmcy);
const defaultImageSrc = '/images/7.jpg';
const initialFieldValues ={
orderId:0,
dateTime:'',
status:'',
status2:'',
pharmacyName:'',
customerName:'',
patientName:'',
patientAge:'',
address:'',
email:'',
teleNo:'',
customerId:1,
pharmacyId:myphmcy,//data obtaind from the URL have to posted as the pharmacyID when posting.
imageName:'',
imageSrc:'',
imageFile: null
}
export default function Upload(props) {
const {addOrEdit} = props
const {myphmcy} = props
const [values, setValues] = useState(initialFieldValues)
const[errors, setErrors] = useState({})
const handleInputChange= e => {
const {name, value} = e.target;
setValues({
...values,
[name]:value
})
}
const showPreview = e => {
if(e.target.files && e.target.files[0]){
let imageFile = e.target.files[0];
const reader = new FileReader();
reader.onload = x => {
setValues({
...values,
imageFile,
imageSrc : x.target.result
})
}
reader.readAsDataURL(imageFile)
}
else{
setValues({
...values,
imageFile:null,
imageSrc:''
})
}
}
const validate = () => {
let temp = {}
temp.customerName = values.customerName == "" ? false : true;
setErrors(temp)
return Object.values(temp).every(x => x == true)
}
const resetForm = () => {
setValues(initialFieldValues)
document.getElementById('image-uploader').value = null;
}
const handleFormSubmit = e => {
e.preventDefault()
if (validate()){
const formData = new FormData()
formData.append('orderId',values.orderId)
formData.append('dateTime',values.dateTime)
formData.append('status',values.status)
formData.append('status2',values.status2)
formData.append('pharmacyName',values.pharmacyName)
formData.append('customerName',values.customerName)
formData.append('patientName',values.patientName)
formData.append('patientAge',values.patientAge)
formData.append('address',values.address)
formData.append('email',values.email)
formData.append('teleNo',values.teleNo)
formData.append('customerId',values.customerId)
formData.append('pharmacyId',values.pharmacyId)
formData.append('imageName',values.imageName)
formData.append('imageFile',values.imageFile)
addOrEdit(formData, resetForm)
alert("Your file is being uploaded!")
}
}
const applyErrorClass = field => ((field in errors && errors[field] == false) ? ' invalid-field' : '')
return (
<>
<div className="container text-center ">
<p className="lead"></p>
</div>
<form autoComplete="off" noValidate onSubmit={handleFormSubmit}>
<div className="card">
<div className="card-header text-center">Place Your Order Here</div>
<img src={values.imageSrc} className="card-img-top"/>
<div className="card-body">
<div className="form-group">
<input type="file" accept="image/*" className="form-control-file" onChange={showPreview} id="image-uploader"/>
</div>
<div className="form-group">
<input type="datetime-local" className="form-control" placeholder="Date Time" name="dateTime" value={values.dateTime}
onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Enter the prescription items and qty" name="status" value={values.status} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="What are the symptoms?" name="status2" value={values.status2} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Pharmacy Name" name="pharmacyName" value={values.pharmacyName} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className={"form-control" + applyErrorClass('customerName')} placeholder="Your Name" name="customerName" value={values.customerName} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Patient Name" name="patientName" value={values.patientName} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Patient Age" name="patientAge" value={values.patientAge} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Delivery address" name="address" value={values.address} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Your Email" name="email" value={values.email} onChange={ handleInputChange}/>
</div>
<div className="form-group">
<input className="form-control" placeholder="Contact Number" name="teleNo" value={values.teleNo} onChange={ handleInputChange}/>
</div>
<div className="form-group text-center">
<button type="submit" className="btn btn-light">submit</button>
</div>
</div>
</div>
</form>
</>
)
}
This doesn't work well and I can't post the data. Can some genius help me with this?

Issue
For some reason you import the Uploadlist (the default export/import) and name it myphmcy, save this in the initial state, and then never consume the passed myphmcy prop. This pattern tends to lead to stale state as you need to also handle when the prop values update so you can synchronize the state value.
Solution
Storing passed props is anti-pattern in React, just consume the myphmcy prop directly in the handleFormSubmit submit handler. This ensure you are always using the latest myphmcy props value when submitting the form data.
const handleFormSubmit = e => {
e.preventDefault();
if (validate()) {
const formData = new FormData();
formData.append('orderId', values.orderId);
formData.append('dateTime', values.dateTime);
formData.append('status', values.status);
formData.append('status2', values.status2);
formData.append('pharmacyName', values.pharmacyName);
formData.append('customerName', values.customerName);
formData.append('patientName', values.patientName);
formData.append('patientAge', values.patientAge);
formData.append('address', values.address);
formData.append('email', values.email);
formData.append('teleNo', values.teleNo);
formData.append('customerId', values.customerId);
formData.append('pharmacyId', myphmcy) // <-- use the prop directly
formData.append('imageName', values.imageName);
formData.append('imageFile', values.imageFile);
addOrEdit(formData, resetForm) ;
alert("Your file is being uploaded!");
};

You are getting the myphmcy value from props but setting it outside of the function. So it will set only undefined to the pharmacyId. You need to assign the myphmcy value inside the fnuction.

Related

Parsing error: 'return' outside of function

I've recently started maintaining JavaScript code and now i am facing trouble in the returning function in line 39. Can someone please help? I have checked the syntax and can't find anything wrong................................................................???????????????????????????????????????????????????????????????????????
import React, { useState, useEffect} from "react";
import axios from "axios";
import { renderMatches, useNavigate, useParams, } from "react-router-dom";
const Editorg = () => {
let navigate = useNavigate();
const {id} = useParams();
console.log(id);
const [user, setUser] = useState ({
name:"",
poname:"",
type:"",
ostatus:"",
});
const { name, poname, type, ostatus } = user;
const onInputChange = e => {
setUser({...user, [e.target.name]: e.target.value });
};
useEffect(() => {
loadUser();
}, []);
};
const onSubmit = async e => {
e.preventDefault();
await axios.put( 'http://localhost:3001/users/${id}' , user);
navigate.push("/");
};
const loadUser = async () =>{
const result = await axios.get('http://localhost:3001/users/${id}', user);
setUser(result.data);
};
return (
<div className="container">
<div className="w-75 mx-auto shadow p-5">
<h5 className="text-left mb-4"> Edit Organization </h5>
<form onSubmit={e => onSubmit(e)}>
<div className="form-group">
<label> Organization Name </label>
<input type="text" className="form-control" name="name" value={name}
onChange={e => onInputChange(e)}
/>
</div>
<div className="form-group">
<label> Parent Organization Name </label>
<input type="text" className="form-control" name="poname" value={poname}
onChange={e => onInputChange(e)}
/>
</div>
<div className="form-group">
<label> Type </label>
<input type="text" className="form-control" name="type" value={type}
onChange={e => onInputChange(e)}
/></div>
<div className="form-group">
<label> Organization Status </label>
<input type="text" className="form-control" name="ostatus" value={ostatus}
onChange={e => onInputChange(e)}
/></div>
<div className="container">
<div className="btn-6">
<button className="btn btn-danger float-right">Update</button>
</div>
</div>
</form>
</div>
</div>
);
}
Around line 26 you are closing the function too early.
useEffect(() => {
loadUser();
}, []);
}; // <- Move this to the very end
const onSubmit = async e => {
</div>
</div>
);
}; // <- Move to here
You mistakly add extra termination of curly bracket
useEffect(() => {
loadUser();
}, []);
}; // <--- remove this

Uncaught TypeError: Cannot destructure property when using React context

Hello guys so i tried to make global state so the other page can use the state. The problem is i got an error that says:
Login.js:18 Uncaught TypeError: Cannot destructure property 'emailLog' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is undefined.
Im doing this because i want the email from user after logged in and pass them to another page so that i can display the logged in user.
App.js:
export const EmailUser = React.createContext();
function App() {
Axios.defaults.withCredentials = true;
const [invoice, setInvoice] = useState("");
const [currency, setCurrency] = useState("IDR");
const [myFile, setMyFile] = useState("");
const [emailLog, setEmailLog] = useState("");
return (
<EmailUser.Provider value={{ emailLog, setEmailLog }}>
<div className="App">
<BasicExample />
<div className="formInput">
<form method="POST" encType="multipart/form-data" action="http://localhost:3001/upload">
<div className="textUser"></div>
<input
className="inputForm"
defaultValue={emailLog}
type="email"
disabled
name="email"
/>
<input className="inputForm" type="number" placeholder="Invoice No" name="InvoiceNo" />
<input className="inputForm" type="date" name="Invoice_Date" />
<input className="inputForm" type="text" placeholder="Description" name="Description" />
<select
className="selectBox"
name="Currency"
onChange={(e) => {
setCurrency(e.target.value);
}}
>
<option value="IDR">IDR</option>
<option value="USD">USD</option>
<option value="YEN">YEN</option>
</select>
<input className="inputForm" type="number" placeholder="Amount" name="Amount" />
<input
className="custom-file-upload"
multiple
type="file"
name="DocumentFile"
onChange={(e) => {
setMyFile(e.target.value);
}}
/>
<button className="btnSubmit">Submit</button>
</form>
</div>
</div>
</EmailUser.Provider>
);
}
export default App;
Login.js
const Login = () => {
let navigate = useNavigate();
const { emailLog, setEmailLog } = useContext(EmailUser);
const [passwordLog, setPasswordLog] = useState("");
const [loginStatus, setLoginStatus] = useState("");
Axios.defaults.withCredentials = true;
const login = (e) => {
e.preventDefault();
Axios.post("http://localhost:3001/login", {
email: emailLog,
password: passwordLog,
}).then((response) => {
console.log(response);
if (response.data.message) {
alert(response.data.message);
} else {
setLoginStatus(response.data[0].email);
alert("Redirecting");
navigate("/home");
}
});
};
console.log(emailLog);
useEffect(() => {
Axios.get("http://localhost:3001/login").then((response) => {
if (response.data.loggedIn == true) {
setLoginStatus(response.data.email[0].email);
}
});
});
return (
<div>
<img className="wave" src={Wave} />
<img className="wave2" src={WaveV2} />
<div className="wrapper">
<div className="img">{/* <img src={Background}/> */}</div>
<div className="register-content">
<div className="registerForm">
<img src={Avatar} />
<h2 className="title">Welcome</h2>
<div className="input-div one">
<div className="i">
<i className="fas fa-user">
<GrMail />
</i>
</div>
<div className="div">
<input
type="email"
className="input"
placeholder="Email"
required
onChange={(e) => {
setEmailLog(e.target.value);
}}
/>
</div>
</div>
<div className="input-div pass">
<div className="i">
<i className="fas fa-lock">
<AiFillLock />
</i>
</div>
<div className="div">
<input
type="password"
className="input"
placeholder="Password"
required
onChange={(e) => {
setPasswordLog(e.target.value);
}}
/>
</div>
</div>
Don't have an account ?
<button type="submit" className="btn" onClick={login}>
Login
</button>
</div>
</div>
</div>
</div>
);
};
export default Login;
EmailUser context works only with the components that are children of EmailUser.Provider, and it doesn't seem to be the case for Login component. An easy way is to create a separate component, in some EmailUserProvider.js, like so:
import {createContext, useState} from "react"
export const EmailUser = createContext();
export default function EmailUserProvider({children}) {
const [emailLog, setEmailLog] = useState("");
return (
<EmailUser.Provider value={{ emailLog, setEmailLog }}>
{children}
</EmailUser.Provider>
);
}
And make it wrap all the components that would consume it. If I assume all my components and routes are rendered in App and want the context to be global, I would do so:
<EmailUserProvider>
<App/>
</EmailUserProvider>

How do I make a POST request using axios in react?

I am having issues with the axios post request. When I click on the Button, nothing happens. What is supposed to happen is that the data that I enter into the input fields is submitted to the API. However, no redirect or anything happens when I click the Button. I am not sure whether the onClick function in the Button is never being triggered or whether the issue lies with the call of axios and then the useNavigate function. I have tried several different ways of using these function but none worked. It might be a syntactic issue as I am a beginner with react. Any help would be appreciated!
Full Code:
import axios from 'axios';
import React, { useState } from 'react';
import { Container, Button } from 'react-bootstrap';
import { useNavigate } from 'react-router-dom';
const AddContact = () => {
const [first_name, setFirstName] = useState("")
const [last_name, setLastName] = useState("")
const [mobile_number, setMobileNumber] = useState("")
const [home_number, setHomeNumber] = useState("")
const [work_number, setWorkNumber] = useState("")
const [email_address, setEmailAddress] = useState("")
const history = useNavigate();
const AddContactInfo = async () => {
let formField = new FormData();
formField.append('first_name', first_name)
formField.append('last_name', last_name)
formField.append('mobile_number', mobile_number)
formField.append('home_number', home_number)
formField.append('work_number', work_number)
formField.append('email_address', email_address)
await axios.post('http://localhost:8000/api/', {
data: formField
}).then(function (response) {
console.log(response.data);
history('/', { replace: true });
})
}
return (
<div>
<h1>Add contact</h1>
<Container>
<div className="form-group">
<input type="text"
className="form-control form-control-lg"
placeholder="Enter Your First Name"
first_name="first_name"
value={first_name}
onChange={(e) => setFirstName(e.target.value)} />
</div>
<div className="form-group">
<input type="text"
className="form-control form-control-lg"
placeholder="Enter Your Last Name"
last_name="last_name"
value={last_name}
onChange={(e) => setLastName(e.target.value)} />
</div>
<div className="form-group">
<input type="text"
className="form-control form-control-lg"
placeholder="Enter Your Mobile Number"
mobile_number="mobile_number"
value={mobile_number}
onChange={(e) => setMobileNumber(e.target.value)} /></div>
<div className="form-group">
<input type="text"
className="form-control form-control-lg"
placeholder="Enter Your Home Number"
home_number="home_number"
value={home_number}
onChange={(e) => setHomeNumber(e.target.value)} /></div>
<div className="form-group">
<input type="text"
className="form-control form-control-lg"
placeholder="Enter Your Work Number"
work_number="work_number"
value={work_number}
onChange={(e) => setWorkNumber(e.target.value)} /></div>
<div className="form-group">
<input type="text"
className="form-control form-control-lg"
placeholder="Enter Your Email Address"
email_address="email_address"
value={email_address}
onChange={(e) => setEmailAddress(e.target.value)} /></div>
<Button onClick={() => { AddContactInfo(); }}>
Add Contact
</Button>
</Container>
</div >
);
};
export default AddContact;
First rename AddContactInfo to addContactInfo and then:
<Button onClick={addContactInfo}>
Add Contact
</Button>
You should correct the method addContactInfo as below:
const AddContactInfo = () => {
let formField = new FormData();
formField.append('first_name', first_name)
formField.append('last_name', last_name)
formField.append('mobile_number', mobile_number)
formField.append('home_number', home_number)
formField.append('work_number', work_number)
formField.append('email_address', email_address)
axios.post('http://localhost:8000/api/', {
data: formField
}).then(function (response) {
console.log(response.data);
history('/', { replace: true });
})
}
Try This:
<Button onClick={AddContactInfo}>
Add Contact
</Button>
import axios from 'axios';
const url = 'http://localhost:8000/api/';
axios.post(url , formField)
.then(response => {
console.log(response.data);
history('/', { replace: true });
})
.catch(({response}) => {
console.log(response);
});
Try calling the function this way :)
<Button onClick={AddContactInfo}>
Add Contact
</Button>

Maximum update depth exceeded with useEffect & map

I am facing this when I am trying to set form error object. Basically, I want to show the errors below each input field. In response, I am getting an array of objects how do I set to my error object?
Error - Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
import axios from "axios";
import React, { useState, useEffect, useCallback } from "react";
import { Link } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { register } from "../actions/userActions";
const Register = () => {
const [countriesList, setCountriesList] = useState("");
const [userRegistration, setUserRegistration] = useState({
firstName: "",
lastName: "",
email: "",
password: "",
fullAddress: "",
city: "",
zipCode: "",
country: "",
phone: "",
terms: true,
});
const [userRegistrationError, setUserRegistrationError] = useState({
firstNameError: "",
lastNameError: "",
emailError: "",
passwordError: "",
fullAddressError: "",
cityError: "",
zipCodeError: "",
countryError: "",
phoneError: "",
termsError: "",
});
const dispatch = useDispatch();
const userRegister = useSelector((state) => state.userRegister);
const { loading, errors, success } = userRegister;
useEffect(() => {
const countries = async () => {
try {
const { data } = await axios.get(
`https://restcountries.eu/rest/v2/all`
);
setCountriesList(data);
} catch (err) {
console.error(err);
}
};
countries();
}, []);
useEffect(() => {
const handleErrors = (errors) => {
errors.map((error) => {
if (error.param === "firstname") {
setUserRegistrationError({
...userRegistrationError,
firstNameError: error.msg,
});
}
if (error.param === "email") {
setUserRegistrationError({
...userRegistrationError,
emailError: error.msg,
});
}
return null;
});
};
if (errors) {
handleErrors(errors);
}
}, [errors, setUserRegistrationError]);
const handleChange = (e) => {
const name = e.target.name;
const value = e.target.value;
setUserRegistration({ ...userRegistration, [name]: value });
};
const handleChkChange = (e) => {
const checked = e.target.checked;
console.log(checked);
setUserRegistration({ ...userRegistration, terms: checked });
};
const handleSubmit = (e) => {
e.preventDefault();
try {
dispatch(register());
} catch (error) {
console.error(error);
}
};
return (
<div className="form_container">
<form action="" onSubmit={handleSubmit}>
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<div className="form-group">
<input
type="text"
name="firstName"
className="form-control"
placeholder="First Name*"
value={userRegistration.firstName}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.firstNameError &&
userRegistrationError.firstNameError}
</p>
</div>
</div>
</div>
<div className="col-6 pr-1">
<div className="form-group">
<input
type="text"
className="form-control"
name="lastName"
placeholder="Last Name*"
value={userRegistration.lastName}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.lastNameError &&
userRegistrationError.lastNameError}
</p>
</div>
</div>
</div>
<hr />
<div className="private box">
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<input
type="email"
className="form-control"
name="email"
id="email_2"
placeholder="Email*"
value={userRegistration.email}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.emailError &&
userRegistrationError.emailError}
</p>
</div>
</div>
<div className="col-6 pl-1">
<div className="form-group">
<input
type="password"
className="form-control"
name="password"
id="password_in_2"
placeholder="Password*"
value={userRegistration.password}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.passwordError &&
userRegistrationError.passwordError}
</p>
</div>
</div>
<div className="col-12">
<div className="form-group">
<input
type="text"
name="fullAddress"
className="form-control"
placeholder="Full Address*"
value={userRegistration.fullAddress}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.fullAddressError &&
userRegistrationError.fullAddressError}
</p>
</div>
</div>
</div>
{/* /row */}
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="City*"
name="city"
value={userRegistration.city}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.cityError &&
userRegistrationError.cityError}
</p>
</div>
</div>
<div className="col-6 pl-1">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Postal Code*"
name="zipCode"
value={userRegistration.zipCode}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.zipCodeError &&
userRegistrationError.zipCodeError}
</p>
</div>
</div>
</div>
{/* /row */}
<div className="row no-gutters">
<div className="col-6 pr-1">
<div className="form-group">
<div className="custom-select-form">
<select
className="wide add_bottom_10 form-control"
name="country"
id="country"
value={userRegistration.country}
onChange={handleChange}
>
<option>Country*</option>
{countriesList &&
countriesList.map((country) => (
<option
key={country.alpha2Code}
value={country.alpha2Code}
>
{country.name}
</option>
))}
</select>
<p className="form-vald-error">
{userRegistrationError.countryError &&
userRegistrationError.countryError}
</p>
</div>
</div>
</div>
<div className="col-6 pl-1">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Telephone *"
name="phone"
value={userRegistration.phone}
onChange={handleChange}
/>
<p className="form-vald-error">
{userRegistrationError.phoneError &&
userRegistrationError.phoneError}
</p>
</div>
</div>
</div>
{/* /row */}
</div>
<hr />
<div className="form-group">
<label className="container_check">
Accept <Link to="#0">Terms and conditions</Link>
<input
type="checkbox"
name="terms"
checked={userRegistration.terms}
onChange={handleChkChange}
/>
<span className="checkmark" />
<p className="form-vald-error">
{userRegistrationError.termsError &&
userRegistrationError.termsError}
</p>
</label>
</div>
<div className="text-center">
<input
type="submit"
defaultValue="Register"
className="btn_1 full-width"
/>
</div>
</form>
</div>
);
};
export default Register;
your effect depends on userRegistrationError which is an object, reference based. Each time useEffect runs,setUserRegistrationError
creates a new object reference, which leads to an infinite loop since references won't be the same as the previous one.
One approach to avoid this issue and keep the right references, is to pass a callback function to setUserRegistrationError instead than a value. This way userRegistrationError is no longer a dependency, it will be an argument to your function instead:
useEffect(() => {
const handleErrors = (errors) => {
errors.forEach((error) => {
if (error.param === "firstName") {
// here you pass a callback function instead, and userRegistrationError is no longer a dependency
// and returns the next state as expected
setUserRegistrationError(userRegistrationError => ({
...userRegistrationError,
firstNameError: error.msg,
}));
}
if (error.param === "email") {
setUserRegistrationError(userRegistrationError => ({
...userRegistrationError,
emailError: error.msg,
}));
}
});
};
if (errors) {
handleErrors(errors);
}
}, [errors, setUserRegistrationError]);
You have a problem with the second useEffect, the first time you update your state userRegistrationError, the component re-rendered and re-executed the useeffect because the dependency userRegistrationError has changed and the process gets repeated again because the state gets updated every render.
useEffect(() => {
const handleErrors = (errors) => {
errors.map((error) => {
if (error.param === "firstname") {
setUserRegistrationError({
...userRegistrationError,
firstNameError: error.msg,
});
}
if (error.param === "email") {
setUserRegistrationError({
...userRegistrationError,
emailError: error.msg,
});
}
return null;
});
};
if (errors) {
handleErrors(errors);
}
}, [errors, setUserRegistrationError ]); //replace userRegistrationError by setUserRegistrationError

How to update state if I have props coming from parent component in React Hooks

I am trying to get updated textbox value if I have props from parent component.
Here is my code -
const initialState = {
name: "",
age: 0
}
const UserAdd = (props:any) => {
const {selectedUser} = props;
const [state, setState] = useState<any>(initialState);
const dispatch = useDispatch();
const onChangeValue = (event:any) => {
const { name, value } = event.target;
setState((prevState:any) => (
{ ...prevState, [name]: value }
));
}
const onSubmit = (e:any) => {
e.preventDefault();
const { name } = e.target;
dispatch(addUser(state.name, state.age))
setState({ ...initialState });
}
const onUpdate = (e:any) => {
e.preventDefault();
const { name } = e.target;
console.log(state.name , state.age , "name-age")
}
return (
<div className="add-user">
<hr />
<h2>Add User</h2>
<hr />
{ selectedUser ?
(<form className="form-inline">
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="email2" placeholder="Enter user name" name="name" value={selectedUser.name} onChange={onChangeValue} />
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="pwd2" placeholder="Enter user age" name="age" onChange={onChangeValue} value={selectedUser.age} />
<button type="submit" onClick={onUpdate} className="btn btn-primary col-md-2 mb-2">Update</button>
</form>)
:
(
<form className="form-inline">
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="email2" placeholder="Enter user name" name="name" value={state.name} onChange={onChangeValue} />
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="pwd2" placeholder="Enter user age" name="age" onChange={onChangeValue} value={state.age} />
<button type="submit" onClick={onSubmit} className="btn btn-primary col-md-2 mb-2">Submit</button>
</form>
)
}
</div>
)
}
export default UserAdd;
When There is no props i.e (no selectedUser), Then state change working fine and I am able to dispatch action as well. But When Props(selectedUser) is available, then I am unable to edit textbox field & unable to get updated state. Please someone help me.
look deeper into your code.. there is different forms when selectedUser is not falsy...
when there is selected user:
value={selectedUser.name}
shouled be like the other form's input:
value={state.name}
u also need to add useEffect to change the state when the prop is being changed, u can do it like so:
useEffect(()=>{
setState((prevState:any) => (
{ ...prevState, name: selectedUser.name}
));
},[selectedUser.name])
the useEffect then will be executed whenever any item of the dependency list will be different from the last render (in this case each time the selectedUser.name is being changed)
In case there is selectedUser, the value attribute of input field should be reactive. Currently, it is set to props that won't react to onChangeValue handler. Use state in the value attribute (state.name /state.age) and initialize states with selectedUser props value. It might look like below -
const initialState = {
name: "",
age: 0
}
const UserAdd = (props:any) => {
const {selectedUser} = props;
//Button text would be Update or submit that depends on selectedUser.
const btnText = selectedUser ? "Update" : "Submit";
//If it founds selected user then set to it otherwise go with initialState
const [state, setState] = useState<any>(selectedUser || initialState);
const dispatch = useDispatch();
const onChangeValue = (event:any) => {
const { name, value } = event.target;
setState((prevState:any) => (
{ ...prevState, [name]: value }
));
}
const onSubmit = (e:any) => {
e.preventDefault();
const { name } = e.target;
dispatch(addUser(state.name, state.age))
setState({ ...initialState });
}
return (
<div className="add-user">
<hr />
<h2>Add User</h2>
<hr />
<form className="form-inline">
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="email2" placeholder="Enter user name" name="name" value={selectedUser.name} onChange={onChangeValue} />
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="pwd2" placeholder="Enter user age" name="age" onChange={onChangeValue} value={selectedUser.age} />
<button type="submit" onClick={onUpdate} className="btn btn-primary col-md-2 mb-2">{btnText}</button>
</form>
</div>
)
}
export default UserAdd;
Once you set it up like this you don't need to render forms conditionally. you only need one form. I have added a few comments as well. I have created a working POC here https://codesandbox.io/s/propstostate-react-ssirn

Categories