前提・実現したいこと
react-dropzone を用いて
取得した画像やPDF等のファイルデータを扱おうとしました。
ですが、dispatchを通した時にエラーとなってしまい
先に進めず、困っています。
環境
+-- react@17.0.1
+-- react-dom@17.0.1
+-- react-dropzone@11.2.4
+-- react-redux@7.2.2
+-- react-scripts@4.0.1
`-- typescript@3.8.3
npx create-react-app . --template redux-typescript
npm install --save react-dropzone
console に出されるエラーメッセージ
調べてみても、シリアル化できないというような理解しかできず
対応できなくなりました。
A non-serializable value was detected in an action, in the path: `payload.document`. Value: File {path: "gel-capsules-5834023_1280.jpg", name: "gel-capsules-5834023_1280.jpg", lastModified: 1609893376819, lastModifiedDate: Wed Jan 06 2021 09:36:16 GMT+0900 (日本標準時), webkitRelativePath: "", …}lastModified: 1609893376819lastModifiedDate: Wed Jan 06 2021 09:36:16 GMT+0900 (日本標準時) {}name: "gel-capsules-5834023_1280.jpg"path: "gel-capsules-5834023_1280.jpg"size: 148443type: "image/jpeg"webkitRelativePath: ""__proto__: File Take a look at the logic that dispatched this action: {type: "drop/incrementFile", payload: {…}} (See https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants) (To allow non-serializable values see: https://redux-toolkit.js.org/usage/usage-guide#working-with-non-serializable-data)
ブラウザ に出されるエラーメッセージ
Unhandled Rejection (Error): Invariant failed: A state mutation was detected between dispatches, in the path 'drop.files.0.document.lastModifiedDate'. This may cause incorrect behavior. (https://redux.js.org/troubleshooting#never-mutate-reducer-arguments)
該当のソースコード
Drop.tsx
onDrop内、コメントの部分以降のdispatchで引っ掛かります
react
1import React, { useCallback } from 'react'; 2import { useSelector, useDispatch } from 'react-redux'; 3import { incrementFile, selectMyFile } from './dropSlice'; 4import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; 5 6import { 7 Typography, 8 Paper 9} from '@material-ui/core/'; 10 11import { useDropzone } from 'react-dropzone' 12import { MY_FILE } from '../Types'; 13 14// スタイル 15const useStyles = makeStyles((theme: Theme) => 16 createStyles({ 17 root: { 18 display: 'flex', 19 }, 20 paper: { 21 padding: theme.spacing(2), 22 textAlign: 'center', 23 '& > *': { 24 margin: theme.spacing(3), 25 }, 26 }, 27 dropzone: { 28 width: "100%", 29 height: 200, 30 boxSizing: "border-box", 31 borderWidth: 2, 32 borderColor: "#666666", 33 borderStyle: "dashed", 34 borderRadius: 5, 35 verticalAlign: "top", 36 marginRight: "2%", 37 }, 38 }), 39); 40 41// Dropzone 42const acceptFile = ['image/*', '.pdf']; 43const maxFileSize = 10485760; 44 45export function Drop() { 46 const files = useSelector(selectMyFile); 47 const dispatch = useDispatch(); 48 const classes = useStyles(useStyles); 49 50 const onDrop = useCallback((acceptedFiles: File[]) => { 51 52 acceptedFiles.forEach((file) => { 53 console.log(file); 54 55 const setFile: MY_FILE = { 56 title: file.name, 57 document: file,/// null でエラーなし 58 } 59 /// ここから先にてエラー 60 dispatch(incrementFile(setFile)); 61 }) 62 63 }, []) 64 // Dropzone 65 const { getRootProps, getInputProps, isDragActive } 66 = useDropzone({ onDrop, accept: acceptFile, minSize: 0, maxSize: maxFileSize }) 67 68 const filesList = files.map(file => ( 69 <li key={file.title}> 70 {file.title} 71 </li> 72 )); 73 return ( 74 <div> 75 <Paper variant="outlined" elevation={3} className={classes.paper}> 76 <Typography variant="h4">Drop test</Typography> 77 <div> 78 <Paper className={classes.dropzone} {...getRootProps()}> 79 <input {...getInputProps()} /> 80 { 81 isDragActive ? 82 <p>Drop the files here ...</p> : 83 <p>Drag 'n' drop some files here, or click to select files</p> 84 } 85 </Paper> 86 </div> 87 </Paper> 88 <aside> 89 <h4>Files</h4> 90 <ul>{filesList}</ul> 91 </aside> 92 </div> 93 ); 94} 95 96export default Drop; 97
dropSlice.ts
react
1import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2import { RootState } from '../../app/store'; 3import { MY_FILE, DROP_STATE } from '../Types' 4 5const initialState: DROP_STATE = { 6 files: [{ 7 title: "", 8 document: null, 9 } 10 ], 11}; 12 13export const dropSlice = createSlice({ 14 name: 'drop', 15 initialState, 16 reducers: { 17 incrementFile(state, action: PayloadAction<MY_FILE>) { 18 state.files[0].title === '' ? state.files[0] = action.payload : 19 state.files = [...state.files, action.payload] 20 } 21 }, 22}); 23 24export const { incrementFile } = dropSlice.actions; 25 26export const selectMyFile = (state: RootState) => state.drop.files; 27 28export default dropSlice.reducer; 29
Types.ts
react
1 2export interface MY_FILE { 3 title: string; 4 document: File | null; 5} 6 7export interface DROP_STATE { 8 files: MY_FILE[]; 9}
App.tsx
react
1import React from 'react'; 2import { Drop } from './features/drop/Drop'; 3import './App.css'; 4 5function App() { 6 return ( 7 <div className="App"> 8 <Drop /> 9 </div> 10 ); 11} 12 13export default App; 14
あなたの回答
tips
プレビュー