这是个人回忆录。
环境
- 反应:18.2.0
- 下一个:12.2.3
- react-dropzone: 14.2.2
- @chakra-ui/反应:2.2.4
你想做的事
- 将多个图像文件拖放到一个页面上
- 预览所选图像
- 预览图可以删除
- 将所选图像上传到数据库
- 库使用
react-dropzone
完整图像
安装
- 首先,安装
react-dropzone
yarn add react-dropzone
示例代码
内容.tsx
import { Img } from '@chakra-ui/react';
import { useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { useImageNew } from 'usecase/useImageNew';
import styles from './contents.module.scss';
export const Contents = () => {
// ブラウザ表示用の paths
const [previewImagePaths, setPreviewImagePaths] = useState<string[]>();
// upload用の files
const [files, setFiles] = useState<File[]>();
const { imageNewHandler } = useImageNew();
//*********************
/** 選択された画像を処理 */
//*********************
const onDrop = (acceptedFiles: File[]) => {
// 引数で受け取れる値は、File型の配列なので upload用のstateへsetする
setFiles(acceptedFiles);
// ブラウザで画像を表示させるための、一時的なURLをメモリに生成する
// @see https://developer.mozilla.org/ja/docs/Web/API/URL/createObjectURL
const dataUrls = acceptedFiles.map((file) => URL.createObjectURL(file));
// ブラウザ表示用のstateへsetする
setPreviewImagePaths(dataUrls);
};
const { getRootProps, getInputProps } = useDropzone({ onDrop });
//************************************************
/** 削除ボタンをクリックすると、プレビュー画像を削除する */
//************************************************
const handleClickDelete = () => {
setPreviewImagePaths([]);
};
//*********************************
/** アップロードボタンクリック時の処理 */
//*********************************
const handleClickUpload = async () => {
const formData = buildFormData(files);
const response = await imageNewHandler(formData);
// 省略
};
const buildFormData = (files?: File[]) => {
if (!files) {
return new FormData();
}
// DB へアップロードするために、FormData へ append する
// @see https://developer.mozilla.org/ja/docs/Web/API/FormData/Using_FormData_Objects
const formData = new FormData();
files.forEach((file) => formData.append(file.name, file, file.name));
return formData;
};
return (
<>
<div className={styles.contents}>
<div {...getRootProps({ className: 'dropzone' })}>
<input {...getInputProps()} accept="image/*" />
<p>
Drag and drop some image files here, or click to select image files
</p>
</div>
</div>
{previewImagePaths &&
previewImagePaths.map((image, i) => (
<div key={i}>
<Img src={image} />
</div>
))}
<div className={styles.contents__button}>
<button onClick={handleClickDelete}>Delete preview images</button>
</div>
<div className={styles.contents__button}>
<button onClick={handleClickUpload}>Upload images</button>
</div>
</>
);
};
内容.module.scss
.contents {
margin: 0 auto;
border: 2px dashed #ccc;
background-color: #eee;
color: #bbb;
max-width: 600px;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&__button {
margin-top: 20px;
}
}
使用ImageNew.ts
import { ImagePostResponse } from 'entity/dropzone/imagePost';
import baseHttpClient from 'infrastructure/httpClient';
// NOTE: 簡易的なレイヤー
export const useImageNew = () => {
const imageNewHandler = async (formData: FormData) => {
const response = await baseHttpClient.post<ImagePostResponse>(
`/api/image`,
formData,
{
// content-type を multipart/form-data に指定
// @see https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Type
// @see https://developer.mozilla.org/ja/docs/Web/API/FormData/Using_FormData_Objects
headers: {
'content-type': 'multipart/form-data',
},
},
);
return response?.data;
};
return {
imageNewHandler,
};
};
模态模式
- 模式打开模态然后拖放
完整图像
示例代码
内容.tsx
import { Img, useDisclosure } from '@chakra-ui/react';
import { useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { useImageNew } from 'usecase/useImageNew';
import { ImageModal } from '../modal/modal';
import styles from './contents.module.scss';
export const Contents = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
// ブラウザ表示用の paths
const [previewImagePaths, setPreviewImagePaths] = useState<string[]>();
// upload用の files
const [files, setFiles] = useState<File[]>();
const { imageNewHandler } = useImageNew();
//*********************
/** 選択された画像を処理 */
//*********************
const selectImages = (acceptedFiles: File[]) => {
setFiles(acceptedFiles);
const dataUrls = acceptedFiles.map((file) => URL.createObjectURL(file));
setPreviewImagePaths(dataUrls);
};
const handleClickDelete = () => {
setPreviewImagePaths([]);
};
const handleClickUpload = async () => {
const formData = buildFormData(files);
const response = await imageNewHandler(formData);
};
const buildFormData = (files?: File[]) => {
if (!files) {
return new FormData();
}
// DB へアップロードするために、FormData へ append する
const formData = new FormData();
files.forEach((file) => formData.append(file.name, file, file.name));
return formData;
};
const handleClickModal = () => {
onOpen();
};
return (
<div className={styles.contents}>
{previewImagePaths &&
previewImagePaths.map((image, i) => (
<div key={i}>
<Img src={image} />
</div>
))}
<ImageModal
isOpen={isOpen}
onClose={onClose}
selectImages={selectImages}
/>
<div className={styles.contents__button}>
<button onClick={handleClickDelete}>Delete preview images</button>
</div>
<div className={styles.contents__button}>
<button onClick={handleClickUpload}>Upload images</button>
</div>
<div className={styles.contents__button}>
<button onClick={handleClickModal}>Open modal</button>
</div>
</div>
);
};
模态的.tsx
import {
Box,
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from '@chakra-ui/react';
import { useDropzone } from 'react-dropzone';
import styles from './modal.module.scss';
type Props = {
isOpen: boolean;
onClose: () => void;
selectImages: (acceptedFiles: File[]) => void;
};
export const ImageModal = ({ isOpen, onClose, selectImages }: Props) => {
//************************************
/** 選択された画像を処理するイベントを通知 */
//************************************
const onDrop = (acceptedFiles: File[]) => {
selectImages(acceptedFiles);
};
const { getRootProps, getInputProps } = useDropzone({ onDrop });
const handleClickOk = () => {
onClose();
};
return (
<Modal isOpen={isOpen} onClose={onClose} isCentered size={'2xl'}>
<ModalOverlay />
<ModalContent>
<ModalHeader className={styles.modal__header}>画像の選択</ModalHeader>
<ModalBody className={styles.modal__body}>
<div className={styles.modal__contents}>
<div {...getRootProps({ className: 'dropzone' })}>
<input {...getInputProps()} accept="image/*" />
<p>
Drag and drop some image files here, or click to select image
files
</p>
</div>
</div>
</ModalBody>
<ModalFooter>
<Box className={styles.modal__footer}>
<button onClick={handleClickOk}>OK</button>
</Box>
</ModalFooter>
</ModalContent>
</Modal>
);
};
参考
关于 react-dropzone
关于 multipart/form-data
关于表单数据
关于文件阅读器
关于文件/Blob
关于 Base64
关于每个功能
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308622431.html