Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

3 лет назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. ![react-dropzone logo](https://raw.githubusercontent.com/react-dropzone/react-dropzone/master/logo/logo.png)
  2. # react-dropzone
  3. [![npm](https://img.shields.io/npm/v/react-dropzone.svg?style=flat-square)](https://www.npmjs.com/package/react-dropzone)
  4. ![Build Status](https://github.com/react-dropzone/react-dropzone/workflows/Test/badge.svg)
  5. [![codecov](https://img.shields.io/codecov/c/gh/react-dropzone/react-dropzone/master.svg?style=flat-square)](https://codecov.io/gh/react-dropzone/react-dropzone)
  6. [![Open Collective](https://img.shields.io/opencollective/backers/react-dropzone.svg?style=flat-square)](#backers)
  7. [![Open Collective](https://img.shields.io/opencollective/sponsors/react-dropzone.svg?style=flat-square)](#sponsors)
  8. Simple React hook to create a HTML5-compliant drag'n'drop zone for files.
  9. Documentation and examples at https://react-dropzone.js.org. Source code at https://github.com/react-dropzone/react-dropzone/.
  10. ## Installation
  11. Install it from npm and include it in your React build process (using [Webpack](http://webpack.github.io/), [Browserify](http://browserify.org/), etc).
  12. ```bash
  13. npm install --save react-dropzone
  14. ```
  15. or:
  16. ```bash
  17. yarn add react-dropzone
  18. ```
  19. ## Usage
  20. You can either use the hook:
  21. ```jsx static
  22. import React, {useCallback} from 'react'
  23. import {useDropzone} from 'react-dropzone'
  24. function MyDropzone() {
  25. const onDrop = useCallback(acceptedFiles => {
  26. // Do something with the files
  27. }, [])
  28. const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})
  29. return (
  30. <div {...getRootProps()}>
  31. <input {...getInputProps()} />
  32. {
  33. isDragActive ?
  34. <p>Drop the files here ...</p> :
  35. <p>Drag 'n' drop some files here, or click to select files</p>
  36. }
  37. </div>
  38. )
  39. }
  40. ```
  41. **IMPORTANT**: Under the hood, this lib makes use of [hooks](https://reactjs.org/docs/hooks-intro.html), therefore, using it requires React `>= 16.8`.
  42. Or the wrapper component for the hook:
  43. ```jsx static
  44. import React from 'react'
  45. import Dropzone from 'react-dropzone'
  46. <Dropzone onDrop={acceptedFiles => console.log(acceptedFiles)}>
  47. {({getRootProps, getInputProps}) => (
  48. <section>
  49. <div {...getRootProps()}>
  50. <input {...getInputProps()} />
  51. <p>Drag 'n' drop some files here, or click to select files</p>
  52. </div>
  53. </section>
  54. )}
  55. </Dropzone>
  56. ```
  57. **Warning**: On most recent browsers versions, the files given by `onDrop` won't have properties `path` or `fullPath`, see [this SO question](https://stackoverflow.com/a/23005925/2275818) and [this issue](https://github.com/react-dropzone/react-dropzone/issues/477).
  58. Furthermore, if you want to access file contents you have to use the [FileReader API](https://developer.mozilla.org/en-US/docs/Web/API/FileReader):
  59. ```jsx static
  60. import React, {useCallback} from 'react'
  61. import {useDropzone} from 'react-dropzone'
  62. function MyDropzone() {
  63. const onDrop = useCallback((acceptedFiles) => {
  64. acceptedFiles.forEach((file) => {
  65. const reader = new FileReader()
  66. reader.onabort = () => console.log('file reading was aborted')
  67. reader.onerror = () => console.log('file reading has failed')
  68. reader.onload = () => {
  69. // Do whatever you want with the file contents
  70. const binaryStr = reader.result
  71. console.log(binaryStr)
  72. }
  73. reader.readAsArrayBuffer(file)
  74. })
  75. }, [])
  76. const {getRootProps, getInputProps} = useDropzone({onDrop})
  77. return (
  78. <div {...getRootProps()}>
  79. <input {...getInputProps()} />
  80. <p>Drag 'n' drop some files here, or click to select files</p>
  81. </div>
  82. )
  83. }
  84. ```
  85. ## Dropzone Props Getters
  86. The dropzone property getters are just two functions that return objects with properties which you need to use to create the drag 'n' drop zone.
  87. The root properties can be applied to whatever element you want, whereas the input properties must be applied to an `<input>`:
  88. ```jsx static
  89. import React from 'react'
  90. import {useDropzone} from 'react-dropzone'
  91. function MyDropzone() {
  92. const {getRootProps, getInputProps} = useDropzone()
  93. return (
  94. <div {...getRootProps()}>
  95. <input {...getInputProps()} />
  96. <p>Drag 'n' drop some files here, or click to select files</p>
  97. </div>
  98. )
  99. }
  100. ```
  101. Note that whatever other props you want to add to the element where the props from `getRootProps()` are set, you should always pass them through that function rather than applying them on the element itself.
  102. This is in order to avoid your props being overridden (or overriding the props returned by `getRootProps()`):
  103. ```jsx static
  104. <div
  105. {...getRootProps({
  106. onClick: event => console.log(event)
  107. })}
  108. />
  109. ```
  110. In the example above, the provided `{onClick}` handler will be invoked before the internal one, therefore, internal callbacks can be prevented by simply using [stopPropagation](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation).
  111. See [Events](https://react-dropzone.js.org#events) for more examples.
  112. *Important*: if you ommit rendering an `<input>` and/or binding the props from `getInputProps()`, opening a file dialog will not be possible.
  113. ## Refs
  114. Both `getRootProps` and `getInputProps` accept a custom `refKey` (defaults to `ref`) as one of the attributes passed down in the parameter.
  115. This can be useful when the element you're trying to apply the props from either one of those fns does not expose a reference to the element, e.g.:
  116. ```jsx static
  117. import React from 'react'
  118. import {useDropzone} from 'react-dropzone'
  119. // NOTE: After v4.0.0, styled components exposes a ref using forwardRef,
  120. // therefore, no need for using innerRef as refKey
  121. import styled from 'styled-components'
  122. const StyledDiv = styled.div`
  123. // Some styling here
  124. `
  125. function Example() {
  126. const {getRootProps, getInputProps} = useDropzone()
  127. <StyledDiv {...getRootProps({ refKey: 'innerRef' })}>
  128. <input {...getInputProps()} />
  129. <p>Drag 'n' drop some files here, or click to select files</p>
  130. </StyledDiv>
  131. }
  132. ```
  133. If you're working with [Material UI](https://material-ui.com) and would like to apply the root props on some component that does not expose a ref, use [RootRef](https://material-ui.com/api/root-ref):
  134. ```jsx static
  135. import React from 'react'
  136. import {useDropzone} from 'react-dropzone'
  137. import RootRef from '@material-ui/core/RootRef'
  138. function PaperDropzone() {
  139. const {getRootProps, getInputProps} = useDropzone()
  140. const {ref, ...rootProps} = getRootProps()
  141. <RootRef rootRef={ref}>
  142. <Paper {...rootProps}>
  143. <input {...getInputProps()} />
  144. <p>Drag 'n' drop some files here, or click to select files</p>
  145. </Paper>
  146. </RootRef>
  147. }
  148. ```
  149. *Important*: do not set the `ref` prop on the elements where `getRootProps()`/`getInputProps()` props are set, instead, get the refs from the hook itself:
  150. ```jsx static
  151. import React from 'react'
  152. import {useDropzone} from 'react-dropzone'
  153. function Refs() {
  154. const {
  155. getRootProps,
  156. getInputProps,
  157. rootRef, // Ref to the `<div>`
  158. inputRef // Ref to the `<input>`
  159. } = useDropzone()
  160. <div {...getRootProps()}>
  161. <input {...getInputProps()} />
  162. <p>Drag 'n' drop some files here, or click to select files</p>
  163. </div>
  164. }
  165. ```
  166. If you're using the `<Dropzone>` component, though, you can set the `ref` prop on the component itself which will expose the `{open}` prop that can be used to open the file dialog programmatically:
  167. ```jsx static
  168. import React, {createRef} from 'react'
  169. import Dropzone from 'react-dropzone'
  170. const dropzoneRef = createRef()
  171. <Dropzone ref={dropzoneRef}>
  172. {({getRootProps, getInputProps}) => (
  173. <div {...getRootProps()}>
  174. <input {...getInputProps()} />
  175. <p>Drag 'n' drop some files here, or click to select files</p>
  176. </div>
  177. )}
  178. </Dropzone>
  179. dropzoneRef.open()
  180. ```
  181. ## Testing
  182. *Important*: `react-dropzone` makes some of its drag 'n' drop callbacks asynchronous to enable promise based `getFilesFromEvent()` functions. In order to properly test this, you may want to utilize a helper function to run all promises like this:
  183. ```js static
  184. const flushPromises = () => new Promise(resolve => setImmediate(resolve))
  185. ```
  186. Example with [react-testing-library](https://github.com/kentcdodds/react-testing-library):
  187. ```js static
  188. import React from 'react'
  189. import Dropzone from 'react-dropzone'
  190. import { fireEvent, render } from 'react-testing-library'
  191. test('invoke onDragEnter when dragenter event occurs', async () => {
  192. const file = new File([
  193. JSON.stringify({ping: true})
  194. ], 'ping.json', { type: 'application/json' })
  195. const data = mockData([file])
  196. const onDragEnter = jest.fn()
  197. const ui = (
  198. <Dropzone onDragEnter={onDragEnter}>
  199. {({ getRootProps, getInputProps }) => (
  200. <div {...getRootProps()}>
  201. <input {...getInputProps()} />
  202. </div>
  203. )}
  204. </Dropzone>
  205. )
  206. const { container } = render(ui)
  207. const dropzone = container.querySelector('div')
  208. dispatchEvt(dropzone, 'dragenter', data)
  209. await flushPromises(ui, container)
  210. expect(onDragEnter).toHaveBeenCalled()
  211. })
  212. function flushPromises(ui, container) {
  213. return new Promise(resolve =>
  214. setImmediate(() => {
  215. render(ui, { container })
  216. resolve(container)
  217. })
  218. )
  219. }
  220. function dispatchEvt(node, type, data) {
  221. const event = new Event(type, { bubbles: true })
  222. Object.assign(event, data)
  223. fireEvent(node, event)
  224. }
  225. function mockData(files) {
  226. return {
  227. dataTransfer: {
  228. files,
  229. items: files.map(file => ({
  230. kind: 'file',
  231. type: file.type,
  232. getAsFile: () => file
  233. })),
  234. types: ['Files']
  235. }
  236. }
  237. }
  238. ```
  239. *Note*: using [Enzyme](https://airbnb.io/enzyme) for testing is not supported at the moment, see [#2011](https://github.com/airbnb/enzyme/issues/2011).
  240. More examples for this can be found in `react-dropzone`s own [test suites](https://github.com/react-dropzone/react-dropzone/blob/master/src/index.spec.js).
  241. ## Support
  242. ### Backers
  243. Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/react-dropzone#backer)]
  244. <a href="https://opencollective.com/react-dropzone/backer/0/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/0/avatar.svg"></a>
  245. <a href="https://opencollective.com/react-dropzone/backer/1/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/1/avatar.svg"></a>
  246. <a href="https://opencollective.com/react-dropzone/backer/2/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/2/avatar.svg"></a>
  247. <a href="https://opencollective.com/react-dropzone/backer/3/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/3/avatar.svg"></a>
  248. <a href="https://opencollective.com/react-dropzone/backer/4/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/4/avatar.svg"></a>
  249. <a href="https://opencollective.com/react-dropzone/backer/5/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/5/avatar.svg"></a>
  250. <a href="https://opencollective.com/react-dropzone/backer/6/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/6/avatar.svg"></a>
  251. <a href="https://opencollective.com/react-dropzone/backer/7/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/7/avatar.svg"></a>
  252. <a href="https://opencollective.com/react-dropzone/backer/8/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/8/avatar.svg"></a>
  253. <a href="https://opencollective.com/react-dropzone/backer/9/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/9/avatar.svg"></a>
  254. <a href="https://opencollective.com/react-dropzone/backer/10/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/10/avatar.svg"></a>
  255. <a href="https://opencollective.com/react-dropzone/backer/11/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/11/avatar.svg"></a>
  256. <a href="https://opencollective.com/react-dropzone/backer/12/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/12/avatar.svg"></a>
  257. <a href="https://opencollective.com/react-dropzone/backer/13/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/13/avatar.svg"></a>
  258. <a href="https://opencollective.com/react-dropzone/backer/14/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/14/avatar.svg"></a>
  259. <a href="https://opencollective.com/react-dropzone/backer/15/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/15/avatar.svg"></a>
  260. <a href="https://opencollective.com/react-dropzone/backer/16/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/16/avatar.svg"></a>
  261. <a href="https://opencollective.com/react-dropzone/backer/17/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/17/avatar.svg"></a>
  262. <a href="https://opencollective.com/react-dropzone/backer/18/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/18/avatar.svg"></a>
  263. <a href="https://opencollective.com/react-dropzone/backer/19/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/19/avatar.svg"></a>
  264. <a href="https://opencollective.com/react-dropzone/backer/20/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/20/avatar.svg"></a>
  265. <a href="https://opencollective.com/react-dropzone/backer/21/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/21/avatar.svg"></a>
  266. <a href="https://opencollective.com/react-dropzone/backer/22/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/22/avatar.svg"></a>
  267. <a href="https://opencollective.com/react-dropzone/backer/23/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/23/avatar.svg"></a>
  268. <a href="https://opencollective.com/react-dropzone/backer/24/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/24/avatar.svg"></a>
  269. <a href="https://opencollective.com/react-dropzone/backer/25/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/25/avatar.svg"></a>
  270. <a href="https://opencollective.com/react-dropzone/backer/26/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/26/avatar.svg"></a>
  271. <a href="https://opencollective.com/react-dropzone/backer/27/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/27/avatar.svg"></a>
  272. <a href="https://opencollective.com/react-dropzone/backer/28/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/28/avatar.svg"></a>
  273. <a href="https://opencollective.com/react-dropzone/backer/29/website" target="_blank"><img src="https://opencollective.com/react-dropzone/backer/29/avatar.svg"></a>
  274. ### Sponsors
  275. Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/react-dropzone#sponsor)]
  276. <a href="https://opencollective.com/react-dropzone/sponsor/0/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/0/avatar.svg"></a>
  277. <a href="https://opencollective.com/react-dropzone/sponsor/1/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/1/avatar.svg"></a>
  278. <a href="https://opencollective.com/react-dropzone/sponsor/2/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/2/avatar.svg"></a>
  279. <a href="https://opencollective.com/react-dropzone/sponsor/3/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/3/avatar.svg"></a>
  280. <a href="https://opencollective.com/react-dropzone/sponsor/4/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/4/avatar.svg"></a>
  281. <a href="https://opencollective.com/react-dropzone/sponsor/5/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/5/avatar.svg"></a>
  282. <a href="https://opencollective.com/react-dropzone/sponsor/6/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/6/avatar.svg"></a>
  283. <a href="https://opencollective.com/react-dropzone/sponsor/7/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/7/avatar.svg"></a>
  284. <a href="https://opencollective.com/react-dropzone/sponsor/8/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/8/avatar.svg"></a>
  285. <a href="https://opencollective.com/react-dropzone/sponsor/9/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/9/avatar.svg"></a>
  286. <a href="https://opencollective.com/react-dropzone/sponsor/10/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/10/avatar.svg"></a>
  287. <a href="https://opencollective.com/react-dropzone/sponsor/11/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/11/avatar.svg"></a>
  288. <a href="https://opencollective.com/react-dropzone/sponsor/12/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/12/avatar.svg"></a>
  289. <a href="https://opencollective.com/react-dropzone/sponsor/13/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/13/avatar.svg"></a>
  290. <a href="https://opencollective.com/react-dropzone/sponsor/14/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/14/avatar.svg"></a>
  291. <a href="https://opencollective.com/react-dropzone/sponsor/15/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/15/avatar.svg"></a>
  292. <a href="https://opencollective.com/react-dropzone/sponsor/16/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/16/avatar.svg"></a>
  293. <a href="https://opencollective.com/react-dropzone/sponsor/17/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/17/avatar.svg"></a>
  294. <a href="https://opencollective.com/react-dropzone/sponsor/18/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/18/avatar.svg"></a>
  295. <a href="https://opencollective.com/react-dropzone/sponsor/19/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/19/avatar.svg"></a>
  296. <a href="https://opencollective.com/react-dropzone/sponsor/20/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/20/avatar.svg"></a>
  297. <a href="https://opencollective.com/react-dropzone/sponsor/21/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/21/avatar.svg"></a>
  298. <a href="https://opencollective.com/react-dropzone/sponsor/22/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/22/avatar.svg"></a>
  299. <a href="https://opencollective.com/react-dropzone/sponsor/23/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/23/avatar.svg"></a>
  300. <a href="https://opencollective.com/react-dropzone/sponsor/24/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/24/avatar.svg"></a>
  301. <a href="https://opencollective.com/react-dropzone/sponsor/25/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/25/avatar.svg"></a>
  302. <a href="https://opencollective.com/react-dropzone/sponsor/26/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/26/avatar.svg"></a>
  303. <a href="https://opencollective.com/react-dropzone/sponsor/27/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/27/avatar.svg"></a>
  304. <a href="https://opencollective.com/react-dropzone/sponsor/28/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/28/avatar.svg"></a>
  305. <a href="https://opencollective.com/react-dropzone/sponsor/29/website" target="_blank"><img src="https://opencollective.com/react-dropzone/sponsor/29/avatar.svg"></a>
  306. ## License
  307. MIT