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
2 Answers
Reset to default 41.Keep your initial state like this :
{
"name":"Anders",
"package":"Silver",
"email":"[email protected]",
"subject":"fdsaf",
"message":"fdsafas",
"reasons": {
"weightLoss": true,
"strength": true,
"sport": true,
}
}
- 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]}});
}
...
}
- 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]);
本文标签:
版权声明:本文标题:javascript - React js useState hook. How to update state of a json object with an a array in it when a checkbox is clicked - Sta 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741311024a2371651.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论