dev0864
dev0864

Reputation: 535

Best way to store file objects in React Redux store (files upload from DropZone?)

I am currently using Material Ui dropzone to upload multiple files. just wondering what is the best way to add/update files in the redux store. I am currently doing a dispatch action when "onChange" triggers which return all the files on the dropzone and updating the redux state by storing the whole files array which has files object.

please let me know if there is a best way, to handle this.

Upvotes: 12

Views: 16519

Answers (3)

Richard
Richard

Reputation: 51

TLDR: this explains how to solve the problem without redux by using react context in typescript (you can just convert it back if you prefer js). Hope this misses the topic not that much, since I was also stumbling upon this thread some time ago.

In the specific case of handling file uploads in forms, I would suggest to not use the state manager (non-serialized data) and go for react context. With this you could pass up the file / array of files (multiupload) and use where ever you need them (in the scope of the provider ofc) and don't have to drill the props their way to the desired component.

I'm sure there are other ways to solve this, but the more I use react context, the more I had understood how things are working under the hood, and the the usage principle compared to a state manager feels just a stone throw away. But let's remember, with a slightly different scope of requirement, because we wanna upload some files and store it in the browser, maybe even preview before we send them, like #jetpackpony described, and then the following comes handy:

Imagine a dialog component we wanna wrap with a context, so that child components can receive and process data in it's parents scope.

Create context & export our provider:

DialogContextProvider.tsx

import { createContext, useMemo, useState } from 'react'

interface DialogContextValues {
  attachments: File[]
  setAttachments: React.Dispatch<React.SetStateAction<File[]>>
}

export const DialogContext = createContext<DialogContextValues | undefined>(undefined)

const DialogContextProvider = ({ children }: { children: JSX.Element }) => {
  const [attachments, setAttachments] = useState<File[]>([])

  const dialogContextValue = useMemo(
    () => ({ attachments, setAttachments }),
    [attachments, setAttachments],
  )

  return <DialogContext.Provider value={dialogContextValue}>{children}</DialogContext.Provider>
}

export default DialogContextProvider

Setup the context

As a parent of the components which then could get/set it's data.

SomeUIComponent.tsx

import DialogContextProvider from './DialogContextProvider'

export const SomeUIComponent = () => (
  <div>
    <h2>This is out of context</h2>
    <DialogContextProvider>
      <SomeComponentWhichUsesContext />
      <AnotherComponentWhichUsesContext />
    </DialogContextProvider>
  </div>
)

context hook use

Addionally we can use custom hook to retrieve the data more streamlined in the code.

useDialogContext.ts

import { useContext } from 'react'

import { DialogContext } from './DialogContextProvider'

// Custom hook to access the created context
const useDialogContext = () => {
  const context = useContext(DialogContext)
  const attachments = context?.attachments || []
  const setAttachments = context?.setAttachments || (() => [])

  if (!context) {
    throw new Error('useDialogContext must be used within a Dialog')
  }

  return { attachments, setAttachments }
}

export default useDialogContext

later you can consume the values you passed to the context like this

const { attachments, setAttachments } = useDialogContext()

Please note that you should only prefer react context over a state manager in smaller scopes of your app, since it will likely trigger more rerenders than a well configured state manager would do and lacks nice features like unidirectional data flow and a predictable, centralized state.

Upvotes: 1

jetpackpony
jetpackpony

Reputation: 1280

I had a similar problem and ended up using this:

URL.createObjectURL(file)

It returns a url like blob:http://example.com/f331074d-a7f3-4972-9c37-96b490a4dd96 which is just a string and can be used in img tag's src attribute. Not sure what Material Ui dropzone returns, but this works with File objects returned by <input type="file" />

Upvotes: 6

markerikson
markerikson

Reputation: 67479

One of the key rules of Redux is that non-serializable values should not go into the store. Because of that, file objects should not be kept in the Redux store if at all possible.

Upvotes: 15

Related Questions