React js useState 钩子。单击复选框时如何更新其中包含数组的 json 对象的状态

React js useState hook. How to update state of a json object with an a array in it when a checkbox is clicked

我通过 lamda 服务器将我的状态作为 stringified 变量从表单发送到 POST 请求,然后解析它并将其发送到我正在使用发送网格模板的 sendgrid特征。这要求我像这样设置 json 格式,以便遍历一个特定部分(多个复选框),所有这些部分都应该具有相同的键但不同的值,由表单中的 reason="Weight Loss" 设置。这是最终的 json 需要形成的方式。

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

然后我可以施展魔法,遍历复选框中选中的原因

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

现在,如果我用单个键值对离开状态并且没有 data 部分,我就可以正常工作了。 这是我的初始状态。

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

然后我有以下 onChange 事件,该事件将输入字段的名称设置为键,将值或选中状态设置为值。看到这里

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 });
    }
 }

这是我的表格

<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>

然后我将它发送到我的 lamdba 函数

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

现在我的状态在被发送到 lamdbda 函数并被解析后看起来像 json 中的以下状态

{ 
  name: 'Anders',
  package: 'silver',
  email: 'email@email.com',
  subject: 'fdsafa',
  weightLoss: 'on',
  strength: 'on',
  sport: 'on',
  message: 'fdsafasf'
} 

现在我想让我的初始状态看起来像 sendgird 想要的格式,所以这就是我尝试的状态设置。

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

然后我尝试使用以下内容更新检查值的 onChange 事件,我还更新了我的表单,因此它获取了一个用户友好的名称。看到下面这段代码

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

表单更改

  ...
     <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>
   ...

我在Post请求后得到的结果Json是这样的,经过我的尝试。它不更新数据部分。所以结果 Json 格式正确,但没有附加原因。提前感谢您的帮助。

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

正在尝试 Rabi 的回答

  ...

 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);

...

看起来您的新 onChange 在您更新密钥时并未替换所有嵌套值。试试这个:

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}, 
    ]
  }
});

另一种方法是使用 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]);

1.Keep 你的初始状态是这样的:

{ 
   "name":"Anders",
   "package":"Silver",
   "email":"email@email.com",
   "subject":"fdsaf",
   "message":"fdsafas",
   "reasons": {
      "weightLoss": true,
      "strength": true,
      "sport": true,
   }
}
  1. 修改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. 更改表单的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;
}