1

(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

excatholica
  • 35
  • 1
  • 6
  • 2
    Can you also post your tsconfig? You can always use (x as Ytype) or //@ts-ignore if this error is blocking you or if it requires to change original library you can't edit. – Zydnar Dec 19 '19 at 11:53
  • 1
    I can see the mistake - problem is `DropzoneInputProps` has only `refKey` while input needs attributes like `type`, `value` etc. - so you can extend your input props with `state.getInputProps()` but also include props from DetailedHTMLProps, HTMLInputElement> – Zydnar Dec 19 '19 at 12:05
  • I tried to simplify your code [here](https://www.typescriptlang.org/play/?jsx=2&ssl=9&ssc=28&pln=7&pc=38#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wChRJY4ARHMALwgDskAaOAbxrsZYGUYKGEjgBfONlxwAREXQwAtABMezJNIDcpUgHodAOkPakAD0rw0AGxQBnG3ABCt4GloQGauKeFMl95PL6AMK4kCxM8BykcDHMbmBwALxwABSYwJZINgBccACCUFAoAJ4APABiGUgAfACUSdWc0TGiugZGMTFEvkhQKfVReob6zZ2dRDAArlBMqaNjC6XxvCJxdIkcMAAWwDb6a+6i1fMLp5wpNoLCuctqAkJI9YmNUWdvCxPTsymlwExgk0iw0uD30AHMkDAAJL-QEABToNn64h0dRO7xarQxMVKOluLGOb1qWjGrVEQA), and there's no issue on the input field. Could you provide a new, standalone sample? – Tiberiu Maran Dec 19 '19 at 16:12
  • Thanks @lelio-faieta for the edits – excatholica Dec 19 '19 at 22:02
  • 1
    Hi @Zydnar I added this `//@ts-ignore` directive - unfortunately I have to figure out how to prevent it showing up in the output (!) - but it worked. It's a decent workaround and enough to break the logjam anyway. Thanks for your help. – excatholica Dec 19 '19 at 23:30

1 Answers1

1

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} />
kuzdogan
  • 554
  • 7
  • 13