{"id":1832,"date":"2016-12-31T01:00:44","date_gmt":"2016-12-31T06:00:44","guid":{"rendered":"http:\/\/www.phpied.com\/?p=1832"},"modified":"2016-12-31T21:27:08","modified_gmt":"2017-01-01T02:27:08","slug":"fail","status":"publish","type":"post","link":"https:\/\/www.phpied.com\/fail\/","title":{"rendered":"File API Input Layer"},"content":{"rendered":"<p>Every once in a while I feel inspired to create <a href=\"http:\/\/csssprites.com\">a<\/a> <a href=\"https:\/\/www.phpied.com\/category\/smushit\/\">little<\/a> <a href=\"http:\/\/cssshrink.com\">tool<\/a> to \"do one thing\" (tm). But often I get distracted and a little too lazy to get off the ground and forget all about it. So I thought maybe a little helper can, well, help move things along.<\/p>\n<p>Enter <a href=\"https:\/\/github.com\/stoyan\/fail\/\">FAIL<\/a>, short for <em>File API Input Layer<\/em> (yup, totally made up to match the acronym).<\/p>\n<h2>FAIL<\/h2>\n<p>It's a very, very simple blueprint for any single-page tool that needs to read a file (or files) from the user and do something with this file. All client-side, naturally, what <em>cannot<\/em> be done in JavaScript these days?<\/p>\n<p>Here's the thing in <a href=\"\/files\/fail\">action<\/a> - it takes images through drag and drop or though a file input dialog. Then simply shows the images with some data about them:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.phpied.com\/wp-content\/uploads\/2016\/12\/Screen-Shot-2016-12-29-at-7.11.06-PM-1.png\" alt=\"screen-shot-2016-12-29-at-7-11-06-pm\" width=\"650\" \/><\/p>\n<p>FAIL doesn't do anything with the images, it's the job of the future tools that could be using it.<\/p>\n<p><a href=\"\/files\/fail\">DEMO<\/a><\/p>\n<h2>React<\/h2>\n<p>FAIL is written in React. I'm probably still a little old-school and when I have an idea I create a blank test.html and go from there, vanilla-like. But in this case I decided to go against my lazy-bum instinct and use something that can get me off the ground. And allow me to write all the ES2019 I want. Even though this means dreaded SETUP. I hate setting stuff up, kills the mood \ud83d\ude42 But in this case turns out React is just perfect for this type of tool. <\/p>\n<p>I couldn't be bothered with Redux or whatever though, not even my self-grown DIY flux implementation. That would be too much.<\/p>\n<p>I used <code><a href=\"https:\/\/github.com\/facebookincubator\/create-react-app\">create-react-app<\/a><\/code> to get started:<\/p>\n<pre>\r\n$ create-react-app fail\r\n$ cd fail\r\n$ npm start\r\n<\/pre>\n<h2>Code<\/h2>\n<p>I shoved all the JS in one file (can't be bothered) and it still ended up under 100 lines of code. The app's components are composed like:<\/p>\n<pre>\r\n&lt;App&gt;\r\n  &lt;Uploads \/&gt;\r\n  &lt;Results \/&gt;\r\n&lt;\/App&gt;\r\n<\/pre>\n<p><code>App<\/code> is actually the one generated by <code>create-react-app<\/code>. In its <code>render()<\/code> I put:<\/p>\n<pre>\r\nrender() {\r\n  return (\r\n    &lt;div className=\"App\"&gt;\r\n      &lt;div className=\"App-header\"&gt;\r\n        &lt;h1&gt;Upload me some images&lt;\/h1&gt;\r\n        &lt;p&gt;pst, you can just drop them anywhere&lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div className=\"Tool-in\"&gt;\r\n        &lt;Uploads onChange={this.handleUploads.bind(this)} \/&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div className=\"Tool-out\"&gt;\r\n        &lt;Results files={this.state.files} \/&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  );\r\n}\r\n<\/pre>\n<p>Simple, eh?<\/p>\n<p>Now <code>Uploads<\/code> and <code>Results<\/code> are even simpler. They just render something. They don't need to maintain state. So they can be implemented as <em>stateless functional components<\/em>. If you're not familiar with those <a href=\"https:\/\/github.com\/stoyan\/fail\/commit\/8fd2142bc4ea830ab271e82c5bf38a601d88785b\">see this diff<\/a> where I switched from ES class syntax to functional components.<\/p>\n<p><code>Uploads<\/code> is just a file input:<\/p>\n<pre>\r\nconst Uploads = ({onChange}) =&gt;\r\n  &lt;div&gt;\r\n    &lt;label htmlFor=\"files\" className=\"Uploads-select\"&gt;Select files...&lt;\/label&gt;\r\n    &lt;input \r\n      type=\"file\" \r\n      id=\"files\" \r\n      multiple \r\n      accept=\"image\/*\" \r\n      style={{display: 'none'}} \r\n      onChange={onChange}\r\n    \/&gt;\r\n  &lt;\/div&gt;;\r\n<\/pre>\n<p><code>Results<\/code> just loops though the uploaded files to put up a table:<\/p>\n<pre>\r\nconst Results = ({files}) =&gt; {\r\n  if (files.length === 0) {return &lt;span\/&gt;;}\r\n  return (\r\n    &lt;table className=\"Results-table\"&gt;\r\n      &lt;tbody&gt;\r\n      &lt;tr&gt;&lt;th&gt;Image&lt;\/th&gt;&lt;th&gt;filename&lt;\/th&gt;&lt;th&gt;size&lt;\/th&gt;&lt;th&gt;mime&lt;\/th&gt;&lt;\/tr&gt;\r\n      {files.map((f, idx) =&gt; {\r\n        if (!f.type.startsWith('image\/')) {\r\n          return null;\r\n        }\r\n        return (\r\n          &lt;tr key={idx}&gt;\r\n            &lt;td&gt;&lt;img alt={f.name} src={window.URL.createObjectURL(f)} height=\"60\" \/&gt;&lt;\/td&gt;\r\n            &lt;td&gt;{f.name}&lt;\/td&gt;\r\n            &lt;td&gt;{f.size}&lt;\/td&gt;\r\n            &lt;td&gt;{f.type}&lt;\/td&gt;\r\n          &lt;\/tr&gt;\r\n        );\r\n      })}\r\n      &lt;\/tbody&gt;\r\n    &lt;\/table&gt;\r\n  );\r\n}\r\n<\/pre>\n<p>Finally the \"brains\" or the non-render methods of the <code>App<\/code> component:<\/p>\n<pre>\r\nconstructor() {\r\n  super();\r\n  this.state = {\r\n    files: [],\r\n  };\r\n  document.documentElement.ondragenter = e =&gt; e.preventDefault();\r\n  document.documentElement.ondragover = e =&gt; e.preventDefault();\r\n  document.documentElement.ondrop = e =&gt; {\r\n    e.preventDefault();\r\n    this.update(e.dataTransfer.files); \/\/ dropped files\r\n  }\r\n}\r\n\r\nhandleUploads(e) { \r\n  this.update(e.target.files); \/\/ from file input\r\n}\r\n\r\nupdate(moreFiles) {\r\n  const files = Array.from(moreFiles);\r\n  if (!files) {\r\n    return;\r\n  }\r\n  this.setState({\r\n    files: this.state.files.concat(files)\r\n  });\r\n}\r\n<\/pre>\n<p>As you can see all we need to do is maintain the list of <code>files<\/code> in the <code>state<\/code> and all comes to place.<\/p>\n<p>The constructor also takes care of setting up drag-drop listeners.<\/p>\n<h2>C'est tout!<\/h2>\n<p>Once again - <a href=\"https:\/\/github.com\/stoyan\/fail\/\">code<\/a> and <a href=\"\/files\/fail\">demo<\/a>.<\/p>\n<p>I'd be thrilled if anyone uses this as a jumping point to create different tools. We can never have too many tools!<\/p>\n<p>Oh yeah - and if you want to <a href=\"http:\/\/www.amazon.com\/dp\/1491931825\/?tag=w3clubs-20\">learn React in 2017 just buy my book<\/a> \ud83d\ude42<\/p>\n<p>Update: <a href=\"https:\/\/www.phpied.com\/create-react-app-sw-precache-pwa\/\">Part 2 where the app becomes a PWA<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Every once in a while I feel inspired to create a little tool to &#8220;do one thing&#8221; &#8482;. But often I get distracted and a little too lazy to get off the ground and forget all about it. So I thought maybe a little helper can, well, help move things along. Enter FAIL, short for [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[5,366,22],"tags":[],"_links":{"self":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts\/1832"}],"collection":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/comments?post=1832"}],"version-history":[{"count":0,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts\/1832\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/media?parent=1832"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/categories?post=1832"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/tags?post=1832"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}