Reputation: 21
I am making a single object out of three independently populated subsections of a form. They are independently populated because the data comes from three different sources:
1) backend api for name and address 2) third party api for telephone number 3) user executing onChange on other fields in the form
what I have noticed is if I submit the form with onChange being the last function executed, ie if entering a note or updating an email address, then the other two elements have some of the data removed specifically the first field of each source.
I have remedied this by creating an effect that basically runs a form control operation (basically resets the phone number) and doing this fixes the problem, but clearly I would not like to not have to rely on a a useState method and call it something its not for the sake of fixing a problem I don't understand. Here is some code.
Thanks!
const leadContext = useContext(LeadContext);
const { clearLiens, lien, setLien, letCall, number, clearNumber, addLead, postLogics } = leadContext;
useEffect(() => {
if (lien !== null) {
setRecord(lien);
}else {
setRecord({
name: '',
address:'',
city:'',
state:'',
zip:'',
plaintiff:'',
amount:''
});
}
}, [lien, leadContext]);
useEffect (()=>{
if(number !== null){
setCall({phone:number});
}else {
setCall({phone:''});
}
},[number, leadContext]);
const [ record, setRecord ] = useState({
name: '',
address:'',
city:'',
state:'',
zip:'',
plaintiff:'',
amount:'',
lienid:''
});
const [ call, setCall ] = useState({
phone: ''});
const [ open, setOpen ] = useState({
email:'',
lexId:'',
compliant:'filed',
filingStatus:'married',
cpa: 'cpa',
ssn:'',
noteText:''
});
const onChange = e => {
setRecord({...name, address, city, state, zip, plaintiff, amount, [e.target.name]: e.target.value });
setCall({...phone, [e.target.name]: e.target.value });
setOpen({...email, lexId, compliant, filingStatus, cpa, ssn, noteText, [e.target.name]: e.target.value});
}
const { name, address, city, state, zip, plaintiff, amount, lienid } = record
const { phone } = call
const { email, lexId, compliant, filingStatus, cpa, ssn, noteText } = open
const lead = {phone, name, address, city, state, zip, plaintiff, amount, lienid, email, lexId, compliant, filingStatus, cpa, ssn, noteText }
const clearLead = () => {
clearNumber();
setLien('');
setRecord({
name: '',
address:'',
city:'',
state:'',
zip:'',
plaintiff:'',
amount:'',
});
setCall({
phone: ''});
setOpen({
email:'',
lexId:'',
compliant:'filed',
filingStatus:'m',
cpa: 'cpa',
noteText:'',
ssn:''
});
}
const onSubmit = e => {
e.preventDefault();
addLead(lead);
clearAll();
};
const clearAll = () => {
clearLiens();
clearLead();
};
const onClick = e => {
letCall(number);
}
the letCall(number) is my hot fix of basically calling set state on one of the form fields. I cannot stack this into my on submit either, so it has to be done as a separate function.
const addLead = async lead => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const { phone, name, address, city, state, zip, plaintiff, amount, lienid, email, lexId, compliant, filingStatus, cpa, ssn, noteText } = lead
const noteId = uuidv4();
const notes = [{ id : noteId,
note : noteText,
notePostedBy: ''
}]
const steve = {phone, name, address, city, state, zip, plaintiff, amount, lienid, email, lexId, compliant, filingStatus, cpa, ssn, notes }
console.log(lead,'1');
console.log(steve,'1');
const res = await axios.post('/api/leads/', steve, config);
dispatch({
type: POST_LEAD,
payload: res.data
});
};
Upvotes: 1
Views: 267
Reputation: 15662
Looks like this has to do with the way you update your state values in onChange
. Specifically, you write:
setRecord({...name, address, city, state, zip, plaintiff, amount, [e.target.name]: e.target.value });
But since name
is not spreadable, it becomes an empty value, so the updated record
state will not have a value for name
(unless name
was the changed input).
To fix this you can simplify your updating with:
const onChange = e => {
setRecord(prev => ({...prev, [e.target.name]: e.target.value}));
setCall(prev => ({...prev, [e.target.name]: e.target.value}));
setOpen(prev => ({...prev, [e.target.name]: e.target.value}));
}
However you probably also want to add some sort of check to update the correct state object so that the keys don't get put into all state objects. Something like:
const onChange = e => {
if (Object.keys(record).includes(e.target.name) {
setRecord(prev => ({...prev, [e.target.name]: e.target.value}));
}
// and so on...
}
Edit:
When using the callback version of set[State]
, the event will become null
since the callback is called asynchronously (specifically at another time). To fix this you can either use:
e.persist();
at the top of the onChange
function (however, probably not optimal in this case).
Or get name
and value
from e.target
and passing them directly to the callback. For example:
const onChange = e => {
const { name, value } = e.target;
if (Object.keys(record).includes(name) {
setRecord(prev => ({...prev, [name]: value}));
}
// and so on...
}
This is probably the most appropriate solution here.
Upvotes: 1