Redux: Delayed file upload... where to put File objects?

Created on 18 Jul 2016  ·  3Comments  ·  Source: reduxjs/redux

I'm looking for a suggestion on handling file uploads. I have a landing page where users can drag and drop a file and are then prompted to sign up before the file upload begins. I need to hold the dragged file object somewhere but since Redux state is only supposed to hold plain JS objects, I'm not sure where to put that file object. I could hold it in some React state/prop but I then have to make sure it is eventually passed to the right component, etc.

Most helpful comment

@olalonde The recommendation that the state should be serializable is a soft one - it's not set it stone. It mainly exists in order to facilitate easy server-side rendering and state snapshotting because you need to be able to serialize your state in order to hydrate it on the client or send it somewhere. As this is purely done on the client it might not be too bad to just go ahead and store that file object short-term for further processing.

And even if you then serialize your state - a File descriptor will be serialize to an object object, so you won't get a serialization error.

All 3 comments

One possible solution would be to listen to the store for state changes using store.subscribe though I'm not sure how to get access to store.subscribe using connect(...). Maybe this.context.store.subscribe()?

@olalonde The recommendation that the state should be serializable is a soft one - it's not set it stone. It mainly exists in order to facilitate easy server-side rendering and state snapshotting because you need to be able to serialize your state in order to hydrate it on the client or send it somewhere. As this is purely done on the client it might not be too bad to just go ahead and store that file object short-term for further processing.

And even if you then serialize your state - a File descriptor will be serialize to an object object, so you won't get a serialization error.

Thanks. I ended up wrapping my upload action creator with the following function:

waitIntroMessageShowedActionCreator.js

// delays execution of `fn` until `state.session.user.ui.introMessageShowed === true`

export const initWaitIntroMessageShowed = ({ getState, subscribe }) => (fn) => () => (
  new Promise((resolve, reject) => {
    const handleChange = () => {
      const state = getState()
      if (!state.session.user.ui) return null
      if (!state.session.user.ui.introMessageShowed) return null
      /* eslint-disable no-use-before-define */
      unsubscribe()
      return Promise
        .resolve()
        .then(() => fn())
        .catch(reject)
        .then(resolve)
    }
    const unsubscribe = subscribe(handleChange)
    handleChange()
  })
)

and in my component:

class DragDropSignup extends Component {
  constructor(props) {
    super(props)
    const waitIntroMessageShowed = initWaitIntroMessageShowed(this.props.store)
    this.onSignupSuccess = waitIntroMessageShowed(this.onSignupSuccess.bind(this))
  }
 // ...
}
// ...

// pass store object as prop
const connectStore = () => (WrappedComponent) => {
  const ConnectStore = (props, context) => (
    <WrappedComponent store={context.store} {...props} />
  )
  ConnectStore.contextTypes = { store: React.PropTypes.object }
  return ConnectStore
}

export default connectStore()(connect()(DragDropSignup))

Posting here in case it's useful to someone

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cloudfroster picture cloudfroster  ·  3Comments

jbri7357 picture jbri7357  ·  3Comments

caojinli picture caojinli  ·  3Comments

ramakay picture ramakay  ·  3Comments

CellOcean picture CellOcean  ·  3Comments