---
title: "Uppy 0.20: React, Retry & Time Travel"
image: "https://uppy.io/images/blog/0.20/dnd-gray.jpg"
date: 2017-10-05
author: renee
published: true
---

We are proud to present Uppy `0.20`. This one focuses on React and Redux support, adding storage expirations to `GoldenRetriever` and upload retries. Enjoy!

## Uppy React components

Uppy now ships with React components! We’ve been exploring different approaches to React components for several months, so we’re excited to finally have them in ✨ There are components for each of Uppy’s UI elements, like the Dashboard and the StatusBar.

```js
const uppy = Uppy()
uppy.use(Tus10, { endpoint: '/upload' })

const Dashboard = require('uppy/lib/react/Dashboard')
const Uploader = () => (
  <Dashboard
    uppy={uppy}
    note="Hey! It's a React component!"
  />
)

ReactDOM.render(<Uploader />, document.querySelector('#uploader'))
```

<!-- more -->

[Check out the docs for more!](/docs/react/)

## Redux

[@richardwillars](https://github.com/richardwillars) contributed a plugin that will sync Uppy's internal state with an existing Redux store.
To use it, define a Redux action and reducer:

```js
// The action creator receives 3 parameters:
// - The previous state
// - The new state
// - The change set
const uppyStateUpdate = (previous, next, patch) => ({
  type: 'UPPY_STATE_UPDATE',
  previous,
  next,
  patch
})

function reduce (state = {}, action) {
  if (action.type === 'UPPY_STATE_UPDATE') {
    return {
      ...state,
      // Merge in the changes.
      ...action.patch
    }
  }
}
```

Then pass your Redux store's `dispatch` function and the action creator to the Redux plugin:

```js
const ReduxStore = require('uppy/lib/Redux')
uppy.use(ReduxStore, {
  dispatch: store.dispatch,
  action: uppyStateUpdate
})
```

[See the docs](/docs/react/redux-sync)

## Redux DevTools

Even if you're not using Redux in your application, there's some Redux-related news! Uppy now has a new plugin, `ReduxDevTools`, which adds debug support for Redux DevTools and allows you to time travel while debugging Uppy:

<figure class="wide"><video alt="Demo video showing Uppy with Redux Dev Tools and time traveling" controls autoplay><source src="/images/blog/0.20/uppy-reduxdevtools.mp4" type="video/mp4">Your browser does not support the video tag, you can <a href="/images/blog/0.20/uppy-reduxdevtools.mp4">download the video</a> to watch it.</video></figure>

To try it, add the plugin:

```js
const ReduxDevTools = require('uppy/lib/plugins/ReduxDevTools')
uppy.use(ReduxDevTools)
```

And then [activate the browser extension](https://github.com/zalmoxisus/redux-devtools-extension).

See [#373](https://github.com/transloadit/uppy/pull/373) for details and discussion.

## Retries

We’ve added a retry UI to the `Dashboard`. If some of your uploads fail—maybe the network is down or the endpoint is unresponsive—you’ll see this:

<img alt="Uppy Dashboard retry UI with individual retry buttons and “retry all”" src="/images/blog/0.20/retry.jpg">

From here it’s easy to retry individual uploads or all at once. This feature currenty works best with `Tus10` plugin, and has basic `XHRUpload` support, but we’ll improve on the latter in the future releases.

## Re-designed Drag and Drop plugin

The old trustly `DragDrop` plugin has been re-designed by [@arturi](https://github.com/arturi) and [@nqst](http://zaytsev.io):

- Fresh simple design, arrow icon, white background;
- New sizing options: `width` and `height` (both `100%` by default to fill the entire container space);
- A `note` option, like in the `Dashboard`, to add info about restrictions or anything else you would like your users to know.

<img class="border" src="/images/blog/0.20/dnd-gray.jpg">

**[Check it out](/examples/dragdrop/)**

```js
uppy.use(DragDrop, {
  target: 'body',
  width: '600px',
  height: '300px',
  note: 'Videos only, up to 100 MB'
})
```

## The Golden Retriever cleans up after itself

We recently released the `GoldenRetriever` plugin, which stores selected files on the client so that it can recover them after a browser crash. Previously, these stored files would stay around forever, and clog up the user's disk space. As of 0.20.0, files will be removed from client-side storage when they have been uploaded. Files that have had nothing happen to them for longer than 24 hours will be cleaned up automatically. This timeframe can be configured using the new `expires` option:

```js
const ms = require('ms')
uppy.use(GoldenRetriever, {
  expires: ms('4 hours')
})
```

(The [`ms`](https://npmjs.com/package/ms) module is great for converting text durations to milliseconds!)

This will clean up files when Uppy runs, but perhaps not every page of your app uses Uppy. If a user selected some files, but then never came back to that same page, files could still hang around for a long time. To aid this, there's a new module that you can call to clean up Uppy's cache without needing an Uppy instance:

```js
const cleanup = require('uppy/lib/plugins/GoldenRetriever/cleanup')
cleanup()
```

We'll hopefully make that require path easier to remember in the future :)

## Misc good stuff

- The GoldenRetriever now detects a serviceWorker registration automatically—it's no longer necessary to emit an `core:sw-file-ready` event.
- Request headers are now configurable in the AWS S3 plugin.
- A new `setPluginState` allows plugins to set state scoped to the plugin.
- Some unused code was removed 🎉
- More tests were added! Thanks [@gavboulton](https://github.com/gavboulton) and [@richardwillars](https://github.com/richardwillars) 🎉
- Documentation for the [StatusBar](/docs/statusbar), [XHRUpload](/docs/xhrupload) and [Tus](/docs/tus) plugins.

## Full Changelog

Here is the full list of changes for version `0.20.0` (and patch `0.19.1`):

- core: retry/error when upload can’t start or fails (offline, connection lost, wrong endpoint); add error in file progress state, UI, question mark button (#307 / @arturi)
- core: support for retry in Tus plugin (#307 / @arturi)
- core: support for retry in XHRUpload plugin (#307 / @arturi)
- core: Add support for Redux DevTools via a plugin (#373 / @arturi)
- core: improve and merge the React PR (#170 / @goto-bus-stop, @arturi)
- core: improve core.log method, add timestamps (#372 / @arturi)
- dragdrop: redesign, add note, width/height options, arrow icon (#374 / @arturi)
- uploaders: upload resolution changes, followup to #323 (#347 / @goto-bus-stop)
- uploaders: issue warning when no uploading plugins are used (#372 / @arturi)
- core: fix `replaceTargetContent` and add tests for `Plugin` (#354 / @gavboulton)
- goldenretriever: Omit completed uploads from saved file state—previously, when an upload was finished and the user refreshed the page, all the finished files would still be there because we saved the entire list of files. Changed this to only store files that are part of an in-progress upload, or that have yet to be uploaded (#358, #324 / @goto-bus-stop)
- goldenretriever: Remove files from cache when upload finished—this uses the deleteBlobs function when core:success fires (#358, #324 / @goto-bus-stop)
- goldenretriever: add a timestamp to cached blobs, and to delete old blobs on boot (#358, #324 / @goto-bus-stop)
- s3: have some way to configure content-disposition for uploads, see #243 (@goto-bus-stop)
- core: move `setPluginState` and add `getPluginState` to `Plugin` class (#363 / @goto-bus-stop)
- goldenretriever: fix restorefiles with id (#351 / @arturi)
- goldenretriever: Clean up blobs that are not related to a file in state (#349 / @goto-bus-stop)
- core: set the newState before emiting `core:state-update` (#341 / @sunil-shrestha, @arturi)
- docs: Document StatusBar plugin (#350 / @goto-bus-stop)

The Uppy Team
