从子组件传递时,FormData 在父组件中为空

Posted

技术标签:

【中文标题】从子组件传递时,FormData 在父组件中为空【英文标题】:FormData is empty in parent when passed from child component 【发布时间】:2022-01-01 18:54:08 【问题描述】:

初学者警报! :) 我在子组件中设置 FormData,然后使用 formReducer 和调度将其传递给父组件,但在父组件中 formData.entries() 始终为空!

ChildComponent.js

function ChildComponent(signed, fileAttached)
    const  dispatch  = useContext(ContactFormContext);

     const changeHandler = (event) => 

const formData = new FormData();

formData.append('File', event.target.files[0]);


dispatch( type: "FILE_ATTACHED", payload: formData )
;

return (
<>
            <div>
        <input type="file" name="file" onChange=changeHandler />
    </div>
</>);

父组件.js

function useFormProgress(fileAttached) 
     
     
    function goForward() 
        const currentStep = 1;

        let appvariables = [
                
                  "key": "PUID",
                  "value": "a2sd"
                ,
                
                  "key": "ApplicationNames",
                  "value": "Trello, abc"
                
              ];
        switch(currentStep) 
          case 0:
            break;
          case 1:
            console.log(fileAttached);
          if(fileAttached != null)
              sendEmail("Resignation Letter", appvariables, fileAttached);
          break;
        
    
    return [goForward];


function sendEmail(templateName, variables, attachment)
  console.log("sending email...");
    const requestBody = 
                    "templateName": templateName,
                    "recipients": [    
                    "abc@xyz.com"
                    ],
                    "variables":  variables,
                    "files": attachment
                ;

fetch('https://localhost:5001/api/Email',
  method:'Post',
  body: JSON.stringify(requestBody),
  headers:'Content-Type': 'application/json',
 );



const initialState = 
      signed: "",
      fileAttached: null
;

function formReducer(state, action) 
   switch (action.type) 
    case "SIGNED":
      return  ...state, signed: action.payload ;
    case "FILE_ATTACHED":
      return ...state, fileAttached: action.payload;
    default:
      throw new Error();
  



function ParentComponent() 

   const [state, dispatch] = useReducer(formReducer, initialState);
     const  signed, fileAttached  = state;

     const steps = [<ChildComponent ...signed, fileAttached/>];

   const [goForward] = useFormProgress(fileAttached);


    return (
        <ContactFormContext.Provider value= dispatch >
          <div>steps[0]
        <div><button onClick=e => 
           e.preventDefault();
              goForward();
        
             
        >  Parent Button
        </button>
        </div>
    </div>
        </ContactFormContext.Provider>
       );

ContactFormContext.js

const ContactFormContext = React.createContext();

在上面的switch语句(ParentComponent)中,console.log(FileAttached)显示FormData有0个条目(见附图),API请求也不成功!

你可以在https://jscomplete.com/playground试试看

    在顶部添加上下文

    添加子组件代码

    添加父组件代码

    添加以下行

      ReactDOM.render(<ParentComponent/>, mountNode);
    

MyAPI 方法

[HttpPost]
    public JsonResult Get(EmailRequest email)
    
         //the request never comes here
     

EmailRequest.cs

public class EmailRequest

    public string TemplateName  get; set; 
    public string[] Recipients  get; set; 
    public List<Variables> Variables  get; set; 
    public FormFileCollection files  get; set; 

【问题讨论】:

在您将 ContactFormContext.Provider 中的 ChildComponent 渲染为子组件的 ParentComponent 中? 抱歉让我补充一下 你能和我们分享一下ContactFormContext吗? 发送上下文文件内容由我模拟到codesandbox中 添加的上下文文件内容它真的是一个声明 - 尝试模仿它的 jsplayground 步骤 - 我还在 c# 中添加我的 api 方法签名 【参考方案1】:

为了通过使用 console.log 从 FormData 的条目方法中获取值,您应该这样做:

  for (var [key, value] of attachment.entries()) 
    console.log("log from for loop", key, value);
  

此外,如果您想使用 POST 请求将文件发送到服务器,则无法对要发送的文件进行字符串化。您当前在 json 有效负载中发送的内容类似于 "files": 。您需要以不同的方式对其进行序列化。这意味着您需要更改发送此文件的方式。 检查这个问题的答案: How do I upload a file with the JS fetch API?

FormData的序列化可以查看这个帖子:https://gomakethings.com/serializing-form-data-with-the-vanilla-js-formdata-object/

【讨论】:

【参考方案2】:

我在代码沙箱中添加了您的代码,一切似乎都很好。

Codesandbox demo

【讨论】:

【参考方案3】:

所以,从上面 Lazar Nikolic 的回答开始,停止尝试 json.stringify!.. 在这里和那里偶然发现一些阻止程序之后,我终于能够成功地将文件发送到 API!

以下是一些需要注意的重要步骤:

在后端 - 我更改了控制器方法:

    添加 [FromForm] 标签

    [HttpPost]
     public JsonResult Get([FromForm] EmailRequest email)
    

This post 帮助了我。

在前端

    将 ChildComponent.js 改成如下

    function ChildComponent(signed, fileAttached)
    const  dispatch  = useContext(ContactFormContext);
    
    const changeHandler = (event) => 
    
    dispatch( type: "FILE_ATTACHED", payload: event.target.files[0] )
    ;
    
    return (
    <>
         <div>
     <input type="file" name="file" onChange=changeHandler />
    </div>
    </>);
    
    

    ParentComponent.js 中的 sendEmail 函数修改如下

    function sendEmail(templateName, variables, attachment)
      console.log("sending email...");
    
      const formData = new FormData();
    
     formData.append('files', attachment);
     formData.append('templateName', templateName);
     formData.append('recipients', [    
      "abc@xyz.com"
     ]);
     formData.append('variables', variables);
    
    
    fetch('https://localhost:5001/api/Email',
    method:'Post',
    body: formData,
    //headers:'Content-Type': 'application/json',
    );
    
    
    

    请注意,在我删除 Content-type 标头之前,收到的电子邮件对象的所有属性都设置为 null,然后浏览器将自己添加 multipart/form-data 标头。我从 @madhu131313 的评论 @ 中得到了帮助987654322@

    我们不能直接在表单数据下传递一个对象数组,所以变量数组是空的..我做了以下

    for(let i = 0; i < variables.length; i++)
     formData.append(`variables[` + i + `].key`, variables[i].key);
     formData.append(`variables[` + i + `].value`, variables[i].value);
    
    

而不是

   formData.append('variables', variables);

并更改收件人如下:

   let recipients = [    
  "abc@xyz.com",
  "nop@xyz.com",
  "stu@xyz.com"
  ];

   for(let i = 0; i < recipients.length; i++)
    formData.append(`recipients[` + i + `]`, recipients[i]);
  

【讨论】:

以上是关于从子组件传递时,FormData 在父组件中为空的主要内容,如果未能解决你的问题,请参考以下文章

在父组件中单击按钮时从子组件执行功能:Angular2

在 Blazor 中将值从子组件传递到父页面的良好做法?

Angular 2:仅当表单数据有效时才将表单数据从子级传递给父级

我如何将 useState 值从子级传递给父级

vuejs 从子组件更新父数据

vuejs 从子组件更新父数据