React Native File Uploads with Axios And Redux Thunk

Istvan Keri
4 min readJul 17, 2019

--

This is the second installment in my series on implementing image uploads with React Native. In my previous article, we created a custom image picker and an upload progress component.

I now want to go over the upload service (thunk action creator) and how to communicate with our nodejs api using Axios. I’m using Redux for state management and Redux Thunk which is great for creating async services that allow you to dispatch multiple action creators, async or not. To clarify I’m calling the thunk methods services because technically action creators should return an object, so I will refer to thunks as services from now on. From within your view component, you can call your thunk upload service like so:

function mapDispatchToProps(dispatch) {
return {
dispatchStorePhoto: photo => {
dispatch(
storePhoto(photo, {
userId: 1,
})
);
},
};
}

So what does the thunk service look like? What will it do? Let’s create a file called S3.service.js. I have this file in a folder called s3 under modules. In this file create a function called storePhoto. This function takes two parameters, photo (CameraRoll photo object) and body (anything else you want to pass along to your API). This function will return a function, hence why its called a thunk. The returned function receives dispatch and getState. Now inside this function, you can use dispatch and also have access to redux state.

export const storePhoto = (photo, body) => (dispatch, getState) => {
const state = getState();
}

It is from here where we want to call our API. First, let’s create a new file called s3.api.js. In this file, we’re going to import some libraries.

Form Data: form-data is an npm package that allows you to create a FormData object key/value pairs representing form fields and their values, which can then be easily sent with XMLHttpRequest, in our case Axios.

Axios: Promise based HTTP client for the browser and node.js

When you have these libraries imported we’ll create a class and a method inside of it. I called this method store, which to me is synonymous with save, and has nothing to do with our redux store.

We’ll need to pass a callback to this method, this is how we’ll hook into our progress bar component. Axios allows us to track upload progress by calling a function and from there we need to dispatch actions to the redux store. Since this class doesn’t have access to dispatch we’ll pass a callback from our thunk.

To create your form data, let’s create a function and pass in the stuff you want to send. See the gist below to see how our FormData is created.

When you’re creating your FormData it’s super important that when you send your file it contains a name and a uri. Without this, your upload won’t work.

Axios returns a promise so we’ll return this promise. If the upload fails we’ll throw an error in the catch statement, that we’ll later catch from our thunk method.

Let’s go back to our thunk. We’ll need to import our new S3.Api.js class and then call our store method. We can catch the thrown error here, for example let’s say our JWT is expired and we need to refresh the token.

return S3Api.store(authToken, photo, body, progressCallback)
.catch(error => {
// If JWT token is expired, let's refresh it
dispatch(handleTokenErrors(error.response.data));
});

We also need to create the progressCallback function. Axios will send the progress object as a parameter, we’ll call it progressEvent. The progress object has information about our file. We can create the percentage loaded using simple algebra.

const progressCallback = progressEvent => { 
const percentFraction = progressEvent.loaded / progressEvent.total;
const percent = Math.floor(percentFraction * 100);
}

From this function, we need to save the percentage loaded in our redux Store. One thing that I’d like to note is that this callback is triggered every time there is an update, which could be hundreds of times. Since we’re building a native application, we don’t want to dispatch over and over again, it could freeze our application, so I decided to throttle the dispatches using a flag mechanism and dispatch when the percentage increments by 10 percent. You can also use debounce from lodash if you want. From the callback, we can update our redux store using dispatches with the file progress information, file size. When the promise returns from Axios the upload is finished! We can now reset our upload state and call it good.

This pretty much sums up this session. Take a look at the finished Thunk method below and feel free to leave a comment if you have any questions.

--

--