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

3

12

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.

Ordinance answered 10/5, 2020 at 1:12 Comment(3)
Your question is very open, What do you need do with the files, do you need process or it is only for send to the server directly ? what are your react and redux version?Phosgenite
I need to send the files to the server post endpoint directly. I am using react 16.13.1 and redux: 4.0.5Ordinance
In this case is much better don't use the redux store for that. You can call to API post directly in the component. There are other similar to dropzone library that maibe you can take a look github.com/pqina/filepond, this include the post directly . Store is more recommended for share serializables between components.Phosgenite
S
14

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.

Subalternate answered 10/5, 2020 at 1:51 Comment(3)
Then just store in local state and send it to actions and make an post API call? I guess this is the only way without adding it to the redux store. Does it sounds good?Ordinance
Largely, yes. Unless there are many other components that really need access to those files anyway, there's not really a good reason to have them in the Redux store in the fist place.Subalternate
One more question, where would be an idle way of making API call to send files to server without a submit button. The method "onChange" gives all the files in the dropzone and method "onDrop" only gives us only files being dropped at that moment. I am thinking of making a API call on "onDrop" please let me know.Ordinance
D
6

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" />

Defenestration answered 17/5, 2021 at 20:20 Comment(3)
How would you send the file to the backend using this?Oliguria
I think this solution only works if you want to preview the image. you don't have access to actual fileLanugo
For retrieving the File/Blob from an URI, see #11876675Millinery
H
1

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.

Hasdrubal answered 20/11, 2023 at 21:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.