3

I use Staticman (staticman.net) for comments on my Gatsby (gatsbyjs.org) site.

I've been using a classic HTML form with method="POST" and action="https://api.staticman.net/..." parameter, as this is what Staticman expects (docs).

However I'd like to make this more "React", and I've changed the form action to a handleSumbit() function:

handleSubmit(event) {
  event.preventDefault()
  fetch("https://api.staticman.net/...", {
    method: "POST",
    body: event.target,
  })
}

I feel that this doesn't work because the API expects an HTTP POST request, with an application/x-www-form-urlencoded content-type, whereas my event.target is a form with loads of React information.

How can I make my fetch() request look exactly like an HTTP POST form submission?

Robin Métral
  • 2,377
  • 13
  • 27

2 Answers2

4

I'd rebuild that POST data from your component state using something like the query-string package. Not sure on the specifics of staticman, but I presume you have form values in your react component's state so you could do something like:

handleSubmit = (event) => {
  event.preventDefault()
  fetch("https://api.staticman.net/...", {
    method: "POST",
    body: queryString.stringify({
      this.state.username,
      this.state.subject
    })
  })
}
BaronVonKaneHoffen
  • 1,535
  • 1
  • 15
  • 22
  • Thank you for the tip on query strings! I ended up finding a way to convert FormData to a query string manually (via JSON). Not perfect, but it's working :) – Robin Métral Jan 21 '19 at 14:59
3

After some research I've found a solution. Here's my handleSubmit function:

handleSubmit = async (event) => {
  event.preventDefault()

  // extract form data
  const formdata = new FormData(event.target)

  // convert FormData to json object
  // SOURCE: https://stackoverflow.com/a/46774073
  const json = {}
  formdata.forEach(function(value, prop){
    json[prop] = value
  })

  // convert json to urlencoded query string
  // SOURCE: https://stackoverflow.com/a/37562814 (comments)
  const formBody = Object.keys(json).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(json[key])).join('&')

  // POST the request to Staticman's API endpoint
  const response = await fetch("https://dev.staticman.net/v3/entry/github/robinmetral/eaudepoisson/master/comments", {
    method: "POST",
    headers: {"Content-Type": "application/x-www-form-urlencoded"},
    body: formBody,
  })
    .then(response => {
      // reset form
      document.getElementById("comment-form").reset()
      // display success message
      document.getElementById("success").style.display = "block"
    })
    .catch(error => {
      console.log(error)
      document.getElementById("failure").style.display = "block"
    })
}

This is what it does:

  1. it extracts the submitted data as a FormData object
  2. it converts the FormData to a JSON object, using a snippet found on SO
  3. it converts the JSON to the expected application/x-www-form-urlencoded content-type or "query string", using another SO snippet
  4. it sends the request to the API endpoint, catches errors and displays a success message

It's probably not the only solution, and if you see something that I could improve, please let me know in the comments or send me a PR!

Robin Métral
  • 2,377
  • 13
  • 27