admin管理员组

文章数量:1287497

I am sending my state as a stringified variable from a form to a POST request through a lamda server which then parses it and sends it to sendgrid which there I am using send grids templating feature. Which requires me to have json Formatted like this in order to loop over one particular part (multiple checkboxes) which all should have the same key but a different value, set by a reason="Weight Loss" in the form. Here is how the eventual json needs to be formed.

{
  "name" :"anders",
  "message" : "winfsdafasfdsfsadfsadnipeg",
  "package" : "silver",
  "email" : "[email protected]",
  "subject" : "fdsafas",
  "data":{
      "reasonArray":[
         {
            "reason":"weightLoss"
         },
         {
            "reason":"Sport"
         }
      ]
   }
}

Then I can do some magic and loop over the reason's that were checked in the checkbox

<ol>
  {{#each data.reasonArray}}
    <li>{{this.reason}} </li>
  {{/each}}
</ol>

Now I had it working if I left the state with single key value pairs and don't have the data portion. Here is what my initial state looked like working.

const [formState, setFormState] = React.useState({
    name: "",
    package: `${data.datoCmsPricing.title}`,
    email: "",
    subject: "",
    weightLoss:"",
    strength:"",
    sport:"",
    message: "",
  })

I then had the following onChange event that set the state with the name of the input field as the key and the value or checked state as the value. Seen here

const onChange = (e) => {
    if (e.target.type === 'checkbox' && !e.target.checked) {
        setFormState({...formState, [e.target.name]: e.target.checked});
    } else {
        setFormState({...formState, [e.target.name]: e.target.value });
    }
 }

and here is my form

<form onSubmit={submitForm}>
      {/* <input type="text" name="package" value={data.datoCmsPricing.title} /> */}
      <label>
        Name
        <input
          type="text"
          name="name"
          value={formState.name}
          onChange={onChange}
        />
      </label>
      <label>
        Email
        <input
          type="email"
          name="email"
          value={formState.email}
          onChange={onChange}
        />
      </label>
      <label>
        Subject
        <input
          type="text"
          name="subject"
          value={formState.subject}
          onChange={onChange}
        />
      </label>
      <div>
        <h3>Reasons for wanting to train</h3>
        <label>
        Weight Loss
        <input 
          type="checkbox"
          name="weightLoss"
          checked={formState.weightLoss}
          onChange={onChange}
        />
        </label>
        <label>
        Strength 
        <input 
          type="checkbox"
          name="strength"
          checked={formState.strength}
          onChange={onChange}
        />
        </label>
        <label>
        Sport 
        <input 
          type="checkbox"
          name="sport"
          checked={formState.sport}
          onChange={onChange}
        />
        </label>
      </div>

      <label>
        message
        <textarea
          name="message"
          value={formState.message}
          onChange={onChange}
        />
      </label>
      <button type="submit">Submit</button>
    </form>

I then send it off to my lamdba function

  const response = await fetch("/lify/functions/sendmail", {
       method: "POST",
       body: JSON.stringify(formState),
     })

Now my state looks like the following in json after being sent to lamdbda function and being parsed

{ 
  name: 'Anders',
  package: 'silver',
  email: '[email protected]',
  subject: 'fdsafa',
  weightLoss: 'on',
  strength: 'on',
  sport: 'on',
  message: 'fdsafasf'
} 

Now I want to have my initial state to look like the format that sendgird wants it in, so this is what I attempted with my state setup.

const [formState, setFormState] = React.useState({
    name: "",
    package: `${data.datoCmsPricing.title}`,
    email: "",
    subject: "",
    weightLoss:"",
    strength:"",
    sport:"",
    message: "",
    data:{
      reasonArray:[
        {
          reason:""
        },
        {
          reason:""
        }
      ]
    }
  })

Then I tried to update the onChange event for the checked values with the following, I also update my form so it grabs a user friendly name for the reason. Seen below this code

 const onChange = (e) => {
    if (e.target.type === 'checkbox' && !e.target.checked) {
        setFormState({...formState, data:{ reasonArray:[ { reason:e.target.reason}, ]}});
    } 
    ...
 }

Form Changes

  ...
     <label>
        Weight Loss
        <input 
          type="checkbox"
          name="weightLoss"
          reason="weightLoss"
          checked={formState.weightLoss}
          onChange={onChange}
        />
        </label>
        <label>
        Strength 
        <input 
          type="checkbox"
          name="strength"
          reason="strength"
          checked={formState.strength}
          onChange={onChange}
        />
        </label>
        <label>
        Sport 
        <input 
          type="checkbox"
          name="sport"
          reason="sport"
          checked={formState.sport}
          onChange={onChange}
        />
        </label>
   ...

The resulting Json I get after the Post request is this, with my attempt. It does not update the data part. So the resulting Json is in the right format, but it doesn't have the reason's attached. Thanks ahead of time for any help.

{ 
   "name":"Anders",
   "package":"Silver",
   "email":"[email protected]",
   "subject":"fdsaf",
   "weightLoss":"on",
   "strength":"on",
   "sport":"on",
   "message":"fdsafas",
   "data":{ 
      "reasonArray":[ 
         { 
            "reason":""
         },
         { 
            "reason":""
         }
      ]
   }
}

Attempting Rabi's answer

  ...

 const prepareDataForApi = (formData) => {
  const newFormData = Object.assign({}, formData); // optional if passed cloned copy of formData object or you can also use lodash cloneDeep
  newFormData.data = {
    reasonArray:[]
  };

  Object.keys(newFormData.reasons).forEach(key => {
    if(newFormData.reasons[key]){
      newFormData.data.reasonArray.push({reason: key})
    }
  });

  delete newFormData.reasons;

  return newFormData;
}



 const submitForm = async (e) => {
  e.preventDefault();

  setForm(false);



  // const newFormData = prepareDataForApi(formData); 
     const newFormData = prepareDataForApi(formState);

  console.log(newFormData);

...

I am sending my state as a stringified variable from a form to a POST request through a lamda server which then parses it and sends it to sendgrid which there I am using send grids templating feature. Which requires me to have json Formatted like this in order to loop over one particular part (multiple checkboxes) which all should have the same key but a different value, set by a reason="Weight Loss" in the form. Here is how the eventual json needs to be formed.

{
  "name" :"anders",
  "message" : "winfsdafasfdsfsadfsadnipeg",
  "package" : "silver",
  "email" : "[email protected]",
  "subject" : "fdsafas",
  "data":{
      "reasonArray":[
         {
            "reason":"weightLoss"
         },
         {
            "reason":"Sport"
         }
      ]
   }
}

Then I can do some magic and loop over the reason's that were checked in the checkbox

<ol>
  {{#each data.reasonArray}}
    <li>{{this.reason}} </li>
  {{/each}}
</ol>

Now I had it working if I left the state with single key value pairs and don't have the data portion. Here is what my initial state looked like working.

const [formState, setFormState] = React.useState({
    name: "",
    package: `${data.datoCmsPricing.title}`,
    email: "",
    subject: "",
    weightLoss:"",
    strength:"",
    sport:"",
    message: "",
  })

I then had the following onChange event that set the state with the name of the input field as the key and the value or checked state as the value. Seen here

const onChange = (e) => {
    if (e.target.type === 'checkbox' && !e.target.checked) {
        setFormState({...formState, [e.target.name]: e.target.checked});
    } else {
        setFormState({...formState, [e.target.name]: e.target.value });
    }
 }

and here is my form

<form onSubmit={submitForm}>
      {/* <input type="text" name="package" value={data.datoCmsPricing.title} /> */}
      <label>
        Name
        <input
          type="text"
          name="name"
          value={formState.name}
          onChange={onChange}
        />
      </label>
      <label>
        Email
        <input
          type="email"
          name="email"
          value={formState.email}
          onChange={onChange}
        />
      </label>
      <label>
        Subject
        <input
          type="text"
          name="subject"
          value={formState.subject}
          onChange={onChange}
        />
      </label>
      <div>
        <h3>Reasons for wanting to train</h3>
        <label>
        Weight Loss
        <input 
          type="checkbox"
          name="weightLoss"
          checked={formState.weightLoss}
          onChange={onChange}
        />
        </label>
        <label>
        Strength 
        <input 
          type="checkbox"
          name="strength"
          checked={formState.strength}
          onChange={onChange}
        />
        </label>
        <label>
        Sport 
        <input 
          type="checkbox"
          name="sport"
          checked={formState.sport}
          onChange={onChange}
        />
        </label>
      </div>

      <label>
        message
        <textarea
          name="message"
          value={formState.message}
          onChange={onChange}
        />
      </label>
      <button type="submit">Submit</button>
    </form>

I then send it off to my lamdba function

  const response = await fetch("/lify/functions/sendmail", {
       method: "POST",
       body: JSON.stringify(formState),
     })

Now my state looks like the following in json after being sent to lamdbda function and being parsed

{ 
  name: 'Anders',
  package: 'silver',
  email: '[email protected]',
  subject: 'fdsafa',
  weightLoss: 'on',
  strength: 'on',
  sport: 'on',
  message: 'fdsafasf'
} 

Now I want to have my initial state to look like the format that sendgird wants it in, so this is what I attempted with my state setup.

const [formState, setFormState] = React.useState({
    name: "",
    package: `${data.datoCmsPricing.title}`,
    email: "",
    subject: "",
    weightLoss:"",
    strength:"",
    sport:"",
    message: "",
    data:{
      reasonArray:[
        {
          reason:""
        },
        {
          reason:""
        }
      ]
    }
  })

Then I tried to update the onChange event for the checked values with the following, I also update my form so it grabs a user friendly name for the reason. Seen below this code

 const onChange = (e) => {
    if (e.target.type === 'checkbox' && !e.target.checked) {
        setFormState({...formState, data:{ reasonArray:[ { reason:e.target.reason}, ]}});
    } 
    ...
 }

Form Changes

  ...
     <label>
        Weight Loss
        <input 
          type="checkbox"
          name="weightLoss"
          reason="weightLoss"
          checked={formState.weightLoss}
          onChange={onChange}
        />
        </label>
        <label>
        Strength 
        <input 
          type="checkbox"
          name="strength"
          reason="strength"
          checked={formState.strength}
          onChange={onChange}
        />
        </label>
        <label>
        Sport 
        <input 
          type="checkbox"
          name="sport"
          reason="sport"
          checked={formState.sport}
          onChange={onChange}
        />
        </label>
   ...

The resulting Json I get after the Post request is this, with my attempt. It does not update the data part. So the resulting Json is in the right format, but it doesn't have the reason's attached. Thanks ahead of time for any help.

{ 
   "name":"Anders",
   "package":"Silver",
   "email":"[email protected]",
   "subject":"fdsaf",
   "weightLoss":"on",
   "strength":"on",
   "sport":"on",
   "message":"fdsafas",
   "data":{ 
      "reasonArray":[ 
         { 
            "reason":""
         },
         { 
            "reason":""
         }
      ]
   }
}

Attempting Rabi's answer

  ...

 const prepareDataForApi = (formData) => {
  const newFormData = Object.assign({}, formData); // optional if passed cloned copy of formData object or you can also use lodash cloneDeep
  newFormData.data = {
    reasonArray:[]
  };

  Object.keys(newFormData.reasons).forEach(key => {
    if(newFormData.reasons[key]){
      newFormData.data.reasonArray.push({reason: key})
    }
  });

  delete newFormData.reasons;

  return newFormData;
}



 const submitForm = async (e) => {
  e.preventDefault();

  setForm(false);



  // const newFormData = prepareDataForApi(formData); 
     const newFormData = prepareDataForApi(formState);

  console.log(newFormData);

...
Share Improve this question edited Dec 17, 2019 at 20:11 Rabi jha 3281 silver badge11 bronze badges asked Dec 17, 2019 at 17:24 Anders KitsonAnders Kitson 1,5456 gold badges44 silver badges114 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 4

1.Keep your initial state like this :

{ 
   "name":"Anders",
   "package":"Silver",
   "email":"[email protected]",
   "subject":"fdsaf",
   "message":"fdsafas",
   "reasons": {
      "weightLoss": true,
      "strength": true,
      "sport": true,
   }
}
  1. Modify onChange():
 const onChange = (e) => {
    if (e.target.type === 'checkbox') {
        const changedReason = e.target.getAttribute('name');
        setFormState({...formState, reasons:{...formState.reasons, [changedReason]: !formState.reasons[changedReason]}});
    } 
    ...
 }

  1. Change form's onSubmit():

    Before calling api , call converter function which will convert formState to JSON format required by your lambda function

const prepareDataForApi = (formData) => {
  const newFormData = Object.assign({}, formData); // optional if passed cloned copy of formData object or you can also use lodash cloneDeep
  newFormData.data = {
    reasonArray:[]
  };

  Object.keys(newFormData.reasons).forEach(key => {
    if(newFormData.reasons[key]){
      newFormData.data.reasonArray.push({reason: key})
    }
  });

  delete newFormData.reasons;

  return newFormData;
}

It looks like your new onChange is not replacing all of your nested values when you update a key. Try this instead:

setFormState({
  ...formState, 
  data:{ 
    ...formState.data, // keep keys from previous data object (not necessary if it only contains the key you are specifying though)
    reasonArray:[ 
      ...formState.data.reasonArray, // keep previous entries from reasonArray
      { reason:e.target.reason}, 
    ]
  }
});

An alternative would be to use an effect.

const [formState, setFormState] = React.useState({...}):

// Runs every time weightLoss is changed
React.useEffect(() => {
  let newReasonArray 
  if (formState.weightLoss) {
    newReasonArray = [...formState.reasonArray]; // So we dont mutate state
    newReasonArray.push({reason: 'weightLoss'});
  } else {
    // If you want to remove it from the array if its unchecked
    newReasonArray = [...formState.reasonArray];
    newReasonArray.filter((reason) => (reason.reason != 'weightLoss'));
  }

  console.log(newReasonArray) // Test if it is updated correctly

  // Set the state with the new array
  setFormState({...formState, data: { reasonArray: newReasonArray }});
}, [formState.weightLoss]);

本文标签: