Mickey Gray
Mickey Gray

Reputation: 21

Object Values Undefined in Post Request from ReactJS

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

Answers (1)

Henry Woody
Henry Woody

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

Related Questions