14 - Upload functionality
Last updated
Last updated
We have enough background to finish the migration of the upload & download functionality of the file.io app. In this section, we'll finish the upload functionality, followed by download functionality in the next section. Together, these two sections will up the purpose of this book.
Just to recap, the upload functionality is:
Provide a button to choose a file for uploading
Call a server API to upload the file
Process the results and show it to the user (file name, size, URL, and generated QR code)
A small anime of the vanilla upload functionality is:
In section 8, we've already decomposed the file.io app into a number of small & manageable components. Here is the diagram again:
In the upload functionality, the Header, MainUpload, and Footer components are always visible. If a file has been uploaded, then FileInfo & UploadAnother component will be visible. If a file has not been uploaded yet, then FileUpload will be visible.
The Upload component code is very straightforward:
Upload.js
The Header, Footer & UploadAnother components are static components. Their code is lifted from vanilla's HTML file. The MainUpload is empty for now. These components also get used in the download functionality. The code of these components is not repeated in the next section.
Headers.js
Footer.js
Footer.css
UploadAnother.js
Let's take a look at the progress so far.
It is good to see that the app is taking shape.
The state will be maintained in the MainUpload component. The state will contain the following data:
fileUploaded: true/false: Indicates if file has been uploaded or not
fileData: This contains information about the uploaded file such as file name, size, and URL.
The QR code doesn't go into state as it gets generated from the file URL.
The state will be initialized to { fileUploaded: false }
. This is the only thing that can go in state because other file attributes are not known till the file has been uploaded.
There is some conditional rendering in MainUpload. If fileUploaded is false, then FileUpload will get rendered. If fileUploaded is true, then FileInfo & UploadAnother will get rendered.
This brings us to the important concept of child updating parent's state. In file.io case, we can see that the state is maintained only by the MainUpload component. No other upload related components maintain state. While MainUpload has the state, the file upload functionality is present in the FileUpload component. Once the file gets uploaded, the FileUpload component needs to update the state in MainUpload so that the components gets re-rendered. This is generally achieved through parent callbacks.
The parent component (MainUpload) will create a function to update the state. It'll pass this function to a relevant child in the props object. When the time comes, the child will update the state in the parent by calling the callback provided by the parent.
The following is the code of the MainUpload component. Note the uploadDone function, which is the callback for updating the state in the parent. The uploadDone function combines the file data that comes from child along with setting fileUploaded to true. The job of the child is to send the file data, not worry about the state, fileUploaded attribute, etc. In the next rendering, the parent component renders FileInfo & drops FileUpload. When the parent renders FileInfo, the parent also passes file data to the FileInfo component.
MainUpload.js
If the code and the explanation doesn't make it clear, the following diagram should help:
The FileUpload component provides a way to upload the file. This component makes use of the useRef hook. The reason was explained in the section 13. We'll not be directly using the default file upload input because it doesn't look the way it looks in the real file.io app. The default file upload input will be hidden. Instead of the file upload input, we'll be using a normal styled button that, on clicking, will simulate a click on the hidden file upload input. We've already seen this in detail towards the end of section 13. The uploaded file is available through the event (like regular JS code event.target.files). The browser fetch API will be used to send the file to the server. Once a response arrives, the parent callback will be used to update the state in parent. The FileUpload component doesn't save anything locally. The parent callback updates state in parent, which, in turn, triggers a re-rendering of parent. This time the rendering will show FileInfo & UploadAnother instead of FileUpload.
FileUpload.js
As soon as the file gets uploaded, the parent components re-renders. This time it'll show FileInfo & UploadAnother. The file data received from FileUpload gets passed to FileInfo which in turn passes:
File name & size to FileDetails component
File URL to FileAccess component
FileInfo.js
The FileDetails component simply displays the received data.
FileDetails.js
FileDetails.css
The FileAccess component uses the file URL to show a QR code. In the vanilla app, we'd used a QR package from CDN. Here, we have already installed QR code as a dependency (from section 9). The QRCode component (provided by that package) can be used as a normal React component.
FileAccess.js
FileAccess.css
You may not realize that we've just finished the upload part of the file.io app. Instead of having a big HTML and a big JS file, we now have very small pieces that clubs together to solve the upload puzzle.
To test it out, first start the express server in a different terminal:
Second, start the webpack server in another terminal:
Open a browser and go to http://localhost:3000:
This looks exactly like the vanilla app. Now, let's do a round of test:
The upload functionality works as expected. In fact, there is hardly any difference from the vanilla app because we're using the same styling.
--
That's all about the first part of the file.io app: Uploading files. The next section covers the downloading part.