Reputation: 35
(edited Title to make more general)
I'm working in a codebase which uses React class components and Typescript everywhere, and I'm trying to implement react-dropzone (a lightweight, simple but effective file-drop component) using the class-based method. The Hooks implementation is cleaner and simpler but I'd prefer to avoid so as to preserve the paradigm maintained in the rest of the code.
In all the README text for this module, it uses plain Javascript. Here's a typical example:
import React from 'react'
import Dropzone from 'react-dropzone'
<Dropzone onDrop={acceptedFiles => console.log(acceptedFiles)}>
{({getRootProps, getInputProps}) => (
<section>
<div {...getRootProps()}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
</section>
)}
</Dropzone>
This seems a fairly unusual syntax anyway...
I've discovered that I can't just use this idiom unchanged in Typescript, so I am trying to get it to work using the following:
import Dropzone, { DropzoneState } from "react-dropzone";
//...
export class BasicDropzone extends React.Component {
onDrop = (files: Array<File>) => {
console.log(files);
this.files = files;
}
//...
render() {
//...
return (
<Dropzone onDrop={this.onDrop}>
{(state: DropzoneState) => {
return (
<section className={styles.container}>
<div {...state.getRootProps({className: styles.dropzone})}>
<input {...state.getInputProps()} />
{/* ^^^^^ error here */}
<p>Drag and drop here, or click to select files</p>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</section>
)}}
</Dropzone>
);
}
}
However the linter throws up the following error on the <input>
tag:
Type '{ refKey?: string | undefined; }' has no properties in common with type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'. ts(2559)
Just for further info, here are the Typescript definitions from the react-dropzone package of relevance:
export type DropzoneState = DropzoneRef & {
isFocused: boolean;
isDragActive: boolean;
isDragAccept: boolean;
isDragReject: boolean;
isFileDialogActive: boolean;
draggedFiles: File[];
acceptedFiles: File[];
rejectedFiles: File[];
rootRef: React.RefObject<HTMLElement>;
inputRef: React.RefObject<HTMLInputElement>;
getRootProps(props?: DropzoneRootProps): DropzoneRootProps;
getInputProps(props?: DropzoneInputProps): DropzoneInputProps;
};
export interface DropzoneRef {
open(): void;
}
export interface DropzoneRootProps extends React.HTMLAttributes<HTMLElement> {
refKey?: string;
[key: string]: any;
}
export interface DropzoneInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
refKey?: string;
}
I'm really not sure how I can fix this - I'm relatively new to Typescript. Other Stackoverflow solutions come close but don't quite fit this issue.
Thanks heaps in advance
@zydnar:
{
"compilerOptions": {
"outDir": "build/dist",
"module": "esnext",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"allowJs": true,
"jsx": "react",
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"experimentalDecorators": true,
"esModuleInterop": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
// See https://github.com/rexxars/react-markdown/issues/207
"allowSyntheticDefaultImports": true,
// See https://stackoverflow.com/questions/52399839/typescript-duplicate-identifier-librarymanagedattributes
// for the reason for this.
"skipLibCheck": true,
"baseUrl": "."
},
"exclude": [
"node_modules",
"build",
"scripts/*.js",
"acceptance-tests",
"webpack",
"jest",
"src/setupTests.ts"
]
}
Also here's a complete example:
import React from "react";
import { observable, action } from 'mobx';
import { observer } from 'mobx-react';
import Dropzone, { DropzoneState } from "react-dropzone";
//const styles = require('./dropzone.css');
@observer
export class BasicDropzone extends React.Component {
@observable files: Array<File> = [];
@action onDrop = (files: Array<File>) => {
console.log(files);
this.files = files;
}
render() {
const files = this.files.map((file: File) => (
<li
key={file.name}
>
{file.name}
</li>
));
return (
<Dropzone onDrop={this.onDrop}>
{(state: DropzoneState) => (
<section className={"container"}>
<div {...state.getRootProps({className: "dropzone"})}>
<input {...state.getInputProps() } />
<p>Drag and drop here, or click to select files</p>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</section>
)}
</Dropzone>
);
}
}
Thanks again
Upvotes: 1
Views: 4332
Reputation: 835
I came across the same issue and was able to circumvent it by providing type
and value
to the <input/>
element as stated in the comments:
<input {...getInputProps()} type="file" value={files} />
Upvotes: 1