Adding Upload to our Amplify React App


In this tutorial, we’ll be learning to upload files to S3 and records to Dynamo using AWS Amplify React App.

In the previous article, we had our app playing songs straight from S3.  To build upon that we first need to create a way to add a song record. We start by creating an Add button.

{
        showAddSong ? <AddSong /> : <IconButton> <AddIcon /> </IconButton>
    }
    

We then also need to add the AddIcon to our imports.

import AddIcon from '@material-ui/icons/Add';
    

Now we can move on to creating the new AddSong component. We can create this at the bottom of our App.jsx file.

const AddSong = () => {
        return (
            <div className="newSong">
                <TextField label="Title" />
                <TextField label="Artist" />
                <TextField label="Description" />
            </div>
        )
    }
    

We also need to add TextField to our imports from material UI.

import { Paper, IconButton, TextField } from '@material-ui/core';
    

The next thing to do is add the ability to open our new component by controlling the showAddSong variable. We need to create a new state declaration next to the others.

const [showAddSong, setShowAddNewSong] = useState(false);
    

We can now update our new AddIcon button to set showAddSong to true.

<IconButton onClick={() => setShowAddNewSong(true)}>
        <AddIcon />
    </IconButton>
    

To change it back, we can add a parameter to our AddSong component called onUpload. When this gets called we will reset the showAddSong to false.

<AddSong
        onUpload={() => {
            setShowAddNewSong(false);
        }}
    />
    

We then need to update our component to work with that new parameter and a button to “upload” the new song. That button calls a function in the component where we will add the ability to upload the data, but for now we will just call the onUpload function.

const AddSong = ({ onUpload }) => {
        const uploadSong = async () => {
            //Upload the song
            onUpload();
        };
    
        return (
            <div className="newSong">
                <TextField
                    label="Title"
                />
                <TextField
                    label="Artist"
                />
                <TextField
                    label="Description"
                />
                <IconButton onClick={uploadSong}>
                    <PublishIcon />
                </IconButton>
            </div>
        );
    };
    

And now we add the PublishIcon to our imports and we’re ready to test this out.

import PublishIcon from '@material-ui/icons/Publish';
    

When we start up the app and log in we now get a plus icon, clicking that we can enter some details for the song and click upload.

Amplify React App

Amplify React App

Updating AddSong

Now we want to be able to store and access the data that a user enters into the fields when adding a song.

const AddSong = ({ onUpload }) => {
        const [songData, setSongData] = useState({});
    
        const uploadSong = async () => {
            //Upload the song
            console.log('songData', songData);
            const { title, description, owner } = songData;
    
            onUpload();
        };
    
        return (
            <div className="newSong">
                <TextField
                    label="Title"
                    value={songData.title}
                    onChange={e => setSongData({ ...songData, title: e.target.value })}
                />
                <TextField
                    label="Artist"
                    value={songData.owner}
                    onChange={e => setSongData({ ...songData, owner: e.target.value })}
                />
                <TextField
                    label="Description"
                    value={songData.description}
                    onChange={e => setSongData({ ...songData, description: e.target.value })}
                />
                <IconButton onClick={uploadSong}>
                    <PublishIcon />
                </IconButton>
            </div>
        );
    };
    

We’ve also had to change all of the TextFields to be controlled, passing in a value from out state and providing an onChange too. If we save this and try entering some details before uploading we should see a console.log of the details in our chrome console.

Next we need to add the ability to actually upload the song. For this we’ll be using the default HTML input with a type of file. Add this to the JSX just before the upload icon button.

<input type="file" accept="audio/mp3" onChange={e => setMp3Data(e.target.files[0])} />
    

As you may have noticed we are calling setMp3Data on change. This is some more state in the AddSong component.

const [mp3Data, setMp3Data] = useState();
    

Now we have all of the data that we need, we can start by uploading the song to S3 and then the data to our database. To upload the song we’re going to use the Amplify React App class again. The fileName is going to be a UUID so we also need to run npm install --save uuid in our terminal and then import it at the top of our file import { v4 as uuid } from 'uuid';. We then pass in the mp3Data and a contentType and we get back an object with a key.

const { key } = await Storage.put(`${uuid()}.mp3`, mp3Data, { contentType: 'audio/mp3' });
    

Now we have the key we can create the record for the song in the database. As there may be multiple songs with the same name, we’ll use an UUID as the ID again.

const createSongInput = {
        id: uuid(),
        title,
        description,
        owner,
        filePath: key,
        like: 0,
    };
    await API.graphql(graphqlOperation(createSong, { input: createSongInput }));
    

To get this to work we need to import the createSong mutator that was created when we created the dynamo storage with Amplify.

import { updateSong, createSong } from './graphql/mutations';
    

The last thing that we need to do is to make the app re-get the data from the database once we’ve finished uploading it. We can do this by adding a fetchSongs call as part of the onUpload function.

<AddSong
        onUpload={() => {
            setShowAddNewSong(false);
            fetchSongs();
        }}
    />

Now when we reload the page, we can click to add a new song, input the details, select our new song, upload it and then play it back from the app

Sam Williams

Sam is a Serverless Obsessive who runs Complete Coding, helping developers learn Serverless and companies make the most of the competitive advantage that Serverless gives them. Previous projects include: - Designing a chat platform that currently resolves over 250,000 customer inquiries a month for international retailers and local government - Architecting a backend system to support a 3D clothing visualisation tool - Building a solution to manage millions of dollars of communication software - Designing a "Video Editing in the Cloud" platform, enabling everyone from movie studios to indie film makers to harness the power of the Cloud - without needing to be cloud experts. - Launching a profitable Serverless Startup in under 30 days He has also been teaching cloud-based software development for 5 years and has taught Serverless development to thousands of people. The Complete Coding Youtube channel now has over 15,000 subscribers and over 1 million views

Recent Posts