Skip to content

Commit

Permalink
feat: implement useReducer for FileUploadContainer state management (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ndv99 authored Feb 20, 2024
1 parent 98e3fa3 commit 4cc442b
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 29 deletions.
4 changes: 2 additions & 2 deletions src/lib/components/FileUpload/FileUpload.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export default meta;
export const Example = {
args: {
error: "",
help: "Max file size is 2GB.",
help: "Max file size is 2MB.",
label: "Upload files",
maxFiles: 7,
maxSize: 2000000000,
maxSize: 2000000,
},
};
41 changes: 14 additions & 27 deletions src/lib/components/FileUpload/FileUpload.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { ReactNode, useCallback, useId, useState } from "react";
import { ReactNode, useId } from "react";

import { Button, Icon, Label } from "@canonical/react-components";
import classNames from "classnames";
import { useDropzone, DropzoneOptions, FileRejection } from "react-dropzone";

import "./FileUpload.scss";
import { useFileUpload } from "./hooks";

import { ProgressIndicator } from "@/lib/elements";

type FileUploadFile = File & { percentUploaded?: number };
export type FileUploadFile = File & { percentUploaded?: number };

export interface FileUploadProps {
accept?: DropzoneOptions["accept"];
Expand All @@ -19,7 +21,7 @@ export interface FileUploadProps {
maxSize?: number;
onFileUpload: NonNullable<DropzoneOptions["onDrop"]>;
rejectedFiles: FileRejection[];
removeFile: (file: File) => void;
removeFile: (file: FileUploadFile) => void;
removeRejectedFile: (fileRejection: FileRejection) => void;
}

Expand Down Expand Up @@ -134,35 +136,20 @@ export const FileUploadContainer = ({
FileUploadProps,
"accept" | "error" | "help" | "label" | "maxFiles" | "maxSize"
>) => {
const [files, setFiles] = useState<File[]>([]);
const [rejectedFiles, setRejectedFiles] = useState<FileRejection[]>([]);

const onFileUpload = useCallback(
(acceptedFiles: File[], fileRejections: FileRejection[]) => {
setFiles([...files, ...acceptedFiles]);
setRejectedFiles([...rejectedFiles, ...fileRejections]);
},
[files, rejectedFiles],
);

const removeFile = (file: File) => {
const newFiles = [...files];
newFiles.splice(newFiles.indexOf(file), 1);
setFiles(newFiles);
};

const removeRejectedFile = (fileRejection: FileRejection) => {
const newRejectedFiles = [...rejectedFiles];
newRejectedFiles.splice(newRejectedFiles.indexOf(fileRejection), 1);
setRejectedFiles(newRejectedFiles);
};
const {
acceptedFiles,
fileRejections,
onFileUpload,
removeFile,
removeRejectedFile,
} = useFileUpload();

return (
<FileUpload
accept={accept}
error={error}
files={files}
rejectedFiles={rejectedFiles}
files={acceptedFiles}
rejectedFiles={fileRejections}
help={help}
label={label}
maxFiles={maxFiles}
Expand Down
85 changes: 85 additions & 0 deletions src/lib/components/FileUpload/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useCallback, useReducer } from "react";

import { FileRejection } from "react-dropzone";

import { FileUploadFile } from "./FileUpload";

type FileUploadState = {
acceptedFiles: FileUploadFile[];
fileRejections: FileRejection[];
};

type AddFiles = {
type: "add-files";
payload: { acceptedFiles: FileUploadFile[]; fileRejections: FileRejection[] };
};
type RemoveAcceptedFile = { type: "remove-accepted"; payload: FileUploadFile };
type RemoveRejectedFile = { type: "remove-rejected"; payload: FileRejection };

type FileUploadActions =
| AddFiles
| RemoveAcceptedFile
| RemoveRejectedFile;

export const useFileUpload = () => {
const fileUploadReducer = (
state: FileUploadState,
action: FileUploadActions,
): FileUploadState => {
switch (action.type) {
case "add-files":
return {
acceptedFiles: [
...state.acceptedFiles,
...action.payload.acceptedFiles,
],
fileRejections: [
...state.fileRejections,
...action.payload.fileRejections,
],
};
case "remove-accepted": {
return {
...state,
acceptedFiles: state.acceptedFiles.filter(
(file) => file !== action.payload,
),
};
}
case "remove-rejected": {
return {
...state,
fileRejections: state.fileRejections.filter(
(rejection) => rejection !== action.payload,
),
};
}
}
};

const [{ acceptedFiles, fileRejections }, dispatch] = useReducer(
fileUploadReducer,
{
acceptedFiles: [],
fileRejections: [],
},
);

const onFileUpload = useCallback(
(acceptedFiles: FileUploadFile[], fileRejections: FileRejection[]) =>
dispatch({
type: "add-files",
payload: { acceptedFiles, fileRejections },
}),
[dispatch],
);

const removeFile = (file: FileUploadFile) =>
dispatch({ type: "remove-accepted", payload: file });

const removeRejectedFile = (fileRejection: FileRejection) =>
dispatch({ type: "remove-rejected", payload: fileRejection });

return { acceptedFiles, fileRejections, onFileUpload, removeFile, removeRejectedFile };

}

0 comments on commit 4cc442b

Please sign in to comment.