Skip to content

Commit a850aa0

Browse files
authored
Improve Hooks Documentation (#3043)
* refactor: break out react-dnd's internal package structure * chore: cut major semver due to re-layout * fix: remove extraneous deps * fix: jest tests * chore: cut semver * fix: tsconfig updates * refactor: roll pkgs back into react-dnd using a flattened folder structure * refactor: update DragDropManager construction; fix tests/linting * chore: update semver document * ci: remove redundant build script * build: nvm * fix: top-level build steps * docs: brush up some api doc pages * docs: move monitoring state towards bottom of article list * chore: cut semver * docs: update alex config
1 parent 0f6e8c5 commit a850aa0

22 files changed

+387
-157
lines changed

.alexrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"allow": ["hook", "hooks"]
3+
}

.yarn/versions/721b19b0.yml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declined:
2+
- react-dnd-documentation

packages/docsite/markdown/docs/00 Quick Start/Overview.md

+22-118
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,6 @@ React DnD is unlike most of the drag and drop libraries out there, and it can be
1010
Some of these concepts resemble the [Flux](http://facebook.github.io/flux/) and [Redux](https://github.com/reactjs/react-redux) architectures.
1111
This is not a coincidence, as React DnD uses Redux internally.
1212

13-
### Backends
14-
15-
React DnD is built on top of the [HTML5 drag and drop API](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_and_drop). It is a reasonable default because it screenshots the dragged DOM node and uses it as a “drag preview” out of the box. It's handy that you don't have to do any drawing as the cursor moves. This API is also the only way to handle the file drop events.
16-
17-
Unfortunately, the HTML5 drag and drop API also has some downsides. It does not work on touch screens, and it provides less customization opportunities on IE than in other browsers.
18-
19-
This is why **the HTML5 drag and drop support is implemented in a pluggable way** in React DnD. You don't have to use it. You can write a different implementation, based on touch events, mouse events, or something else entirely. Such pluggable implementations are called the _backends_ in React DnD. Only the [HTML5 backend](/docs/backends/html5) comes with the library, but more may be added in the future.
20-
21-
The backends perform a similar role to that of React's synthetic event system: **they abstract away the browser differences and process the native DOM events.** Despite the similarities, React DnD backends do not have a dependency on React or its synthetic event system. Under the hood, all the backends do is translate the DOM events into the internal Redux actions that React DnD can process.
22-
2313
### Items and Types
2414

2515
Like Flux (or Redux), React DnD uses data, and not the views, as the source of truth. When you drag something across the screen, we don't say that a component, or a DOM node is being dragged. Instead, we say that an _item_ of a certain _type_ is being dragged.
@@ -97,129 +87,43 @@ Whenever you want to make a component or some part of it draggable, you need to
9787

9888
The _drop targets_ are very similar to the drag sources. The only difference is that a single drop target may register for several item types at once, and instead of producing an item, it may handle its hover or drop.
9989

100-
### Higher-Order Components and Decorators
101-
102-
How do you wrap your components? What does wrapping even mean? If you have not worked with higher-order components before, go ahead and read [this article](https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750), as it explains the concept in detail.
103-
104-
**A higher-order component is a function that takes a React component class, and returns another React component class.** The wrapping component provided by the library renders _your_ component in its `render` method and forwards the props to it, but also adds some useful behavior.
105-
106-
In React DnD, [`DragSource`](/docs/api/drag-source) and [`DropTarget`](/docs/api/drop-target), as well as a few other top-level exported functions, are in fact higher-order components. They breathe the drag and drop magic into your components.
107-
108-
One caveat of using them is that they require _two_ function applications. For example, here's how to wrap `YourComponent` in a [`DragSource`](/docs/api/drag-source):
109-
110-
```jsx
111-
import { DragSource } from 'react-dnd'
112-
113-
class YourComponent {
114-
/* ... */
115-
}
116-
117-
export default DragSource(/* ... */)(YourComponent)
118-
```
90+
### Backends
11991

120-
Notice how, after specifying the [`DragSource`](/docs/api/drag-source) parameters in the first function call, there is a _second_ function call, where you finally pass your class. This is called [currying](http://en.wikipedia.org/wiki/Currying), or [partial application](http://en.wikipedia.org/wiki/Partial_application), and is necessary for the [decorator syntax](http://github.com/wycats/javascript-decorators) to work out of the box:
92+
React DnD uses the [HTML5 drag and drop API](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_and_drop). It is a reasonable default because it screenshots the dragged DOM node and uses it as a “drag preview” out of the box. It's handy that you don't have to do any drawing as the cursor moves. This API is also the only way to handle the file drop events.
12193

122-
```jsx
123-
import { DragSource } from 'react-dnd'
94+
Unfortunately, the HTML5 drag and drop API also has some downsides. It does not work on touch screens, and it provides less customization opportunities on IE than in other browsers.
12495

125-
@DragSource(/* ... */)
126-
export default class YourComponent {
127-
/* ... */
128-
}
129-
```
96+
This is why **the HTML5 drag and drop support is implemented in a pluggable way** in React DnD. You don't have to use it. You can write a different implementation, based on touch events, mouse events, or something else entirely. Such pluggable implementations are called the _backends_ in React DnD.
13097

131-
You don't have to use this syntax, but if you like it, you can enable it by transpiling your code with [Babel](http://babeljs.io), and putting `{ "stage": 1 }` into your [.babelrc file](https://babeljs.io/docs/usage/babelrc/).
98+
The library currently ships with the [HTML backend](docs/backends/html5), which should be sufficient for most web applications. There is also a [Touch backend](/docs/backend/touch) that can be used for mobile web applications.
13299

133-
Even if you don't plan to use decorators, the partial application can still be handy, because you can combine several [`DragSource`](/docs/api/drag-source) and [`DropTarget`](/docs/api/drop-target) declarations in JavaScript using a functional composition helper such as [`_.flow`](https://lodash.com/docs#flow). With decorators, you can stack the decorators to achieve the same effect.
100+
The backends perform a similar role to that of React's synthetic event system: **they abstract away the browser differences and process the native DOM events.** Despite the similarities, React DnD backends do not have a dependency on React or its synthetic event system. Under the hood, all the backends do is translate the DOM events into the internal Redux actions that React DnD can process.
134101

135-
```jsx
136-
import { DragSource, DropTarget } from 'react-dnd'
137-
import flow from 'lodash/flow'
138-
139-
class YourComponent {
140-
render() {
141-
const { connectDragSource, connectDropTarget } = this.props
142-
return connectDragSource(
143-
connectDropTarget()
144-
/* ... */
145-
)
146-
}
147-
}
102+
### Hooks vs Higher-Order Components
148103

149-
export default flow(DragSource(/* ... */), DropTarget(/* ... */))(YourComponent)
150-
```
104+
Now you should have an understanding of the various moving pieces of React DnD:
151105

152-
### Putting It All Together
106+
- Item objects and types
107+
- DnD state via flux
108+
- Monitors for observing DnD state
109+
- Collector functions for turning monitor output into consumable props
110+
- Connectors for attaching the DnD state machine to view nodes (e.g. DOM elements)
153111

154-
Below is an example of wrapping an existing `Card` component into a drag source.
112+
Now let's talk about how these pieces come together in your components. You have two options: a modern hooks-based API and the classic Decorators-based API.
155113

156-
```jsx
157-
import React from 'react'
158-
import { DragSource } from 'react-dnd'
159-
160-
// Drag sources and drop targets only interact
161-
// if they have the same string type.
162-
// You want to keep types in a separate file with
163-
// the rest of your app's constants.
164-
const Types = {
165-
CARD: 'card'
166-
}
114+
### Hooks
167115

168-
/**
169-
* Specifies the drag source contract.
170-
* Only `beginDrag` function is required.
171-
*/
172-
const cardSource = {
173-
beginDrag(props) {
174-
// Return the data describing the dragged item
175-
const item = { id: props.id }
176-
return item
177-
},
178-
179-
endDrag(props, monitor, component) {
180-
if (!monitor.didDrop()) {
181-
return
182-
}
183-
184-
// When dropped on a compatible target, do something
185-
const item = monitor.getItem()
186-
const dropResult = monitor.getDropResult()
187-
CardActions.moveCardToList(item.id, dropResult.listId)
188-
}
189-
}
116+
Modern React applications have replaced the Higher-Order-Component pattern with hooks. Hooks are a feature of React, introduced in 16.8, that allow for developers to write stateful function components. They also fantastic for managing stateful components, and also for interacting with external stateful systems (\***cough**\* like a Drag-and-Drop engine \***cough**\*).
190117

191-
/**
192-
* Specifies which props to inject into your component.
193-
*/
194-
function collect(connect, monitor) {
195-
return {
196-
// Call this function inside render()
197-
// to let React DnD handle the drag events:
198-
connectDragSource: connect.dragSource(),
199-
// You can ask the monitor about the current drag state:
200-
isDragging: monitor.isDragging()
201-
}
202-
}
118+
If you are unfamiliar with React hooks, refer to the React blog post, [Introducing Hooks](https://reactjs.org/docs/hooks-intro.html).
203119

204-
function Card(props) {
205-
// Your component receives its own props as usual
206-
const { id } = props
120+
React-DnD provides hooks that connect your components to the DnD engine, and allow you to collect monitor state for rendering.
207121

208-
// These two props are injected by React DnD,
209-
// as defined by your `collect` function above:
210-
const { isDragging, connectDragSource } = props
122+
For an overview of the hooks-based API, refer to the [Hooks Overview](/docs/api/hooks-overview) page.
211123

212-
return connectDragSource(
213-
<div>
214-
I am a draggable card number {id}
215-
{isDragging && ' (and I am being dragged now)'}
216-
</div>
217-
)
218-
}
124+
### Higher-Order Components and Decorators
219125

220-
// Export the wrapped version
221-
export default DragSource(Types.CARD, cardSource, collect)(Card)
222-
```
126+
### Conclusion
223127

224128
Now you know enough about React DnD to explore the rest of the documentation!
225-
The [tutorial](/docs/tutorial) is a great place to start.
129+
The [hooks overview](/docs/api/hooks-overview) and [decorators overview](/docs/api/decorators-overview) documentation pages are great places to start. Or jump straight into the [tutorial app](/docs/tutorial) and build a chess game!

packages/docsite/markdown/docs/04 Monitoring State/DragLayerMonitor.md renamed to packages/docsite/markdown/docs/02 Monitoring State/DragLayerMonitor.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ _New to React DnD? [Read the overview](/docs/overview) before jumping into the d
77

88
# DragLayerMonitor
99

10-
`DragLayerMonitor` is an object passed to a collecting function of the [`DragLayer`](/docs/api/drag-layer). Its methods let you get information about the global drag state.
10+
`DragLayerMonitor` is an object passed to a collecting function of a [hooks-based](/docs/api/use-drag-layer) or [decorator-based](/docs/api/drag-layer) drag layer. Its methods let you get information about the global drag state.
1111

1212
### Methods
1313

packages/docsite/markdown/docs/04 Monitoring State/DragSourceMonitor.md renamed to packages/docsite/markdown/docs/02 Monitoring State/DragSourceMonitor.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ _New to React DnD? [Read the overview](/docs/overview) before jumping into the d
77

88
# DragSourceMonitor
99

10-
`DragSourceMonitor` is an object passed to a collecting function of the [`DragSource`](/docs/api/drag-source). Its methods let you get information about the drag state of a specific drag source. The specific drag source bound to that monitor is called the monitor's _owner_ below.
10+
`DragSourceMonitor` is an object passed to a collecting function of a [hooks-based](/docs/api/use-drag) or [decorator-based](/docs/api/drag-source) dragging source. Its methods let you get information about the drag state of a specific drag source. The specific drag source bound to that monitor is called the monitor's _owner_ below.
1111

1212
### Methods
1313

packages/docsite/markdown/docs/04 Monitoring State/DropTargetMonitor.md renamed to packages/docsite/markdown/docs/02 Monitoring State/DropTargetMonitor.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ _New to React DnD? [Read the overview](/docs/overview) before jumping into the d
77

88
# DropTargetMonitor
99

10-
`DropTargetMonitor` is an object passed to a collecting function of the [`DropTarget`](/docs/api/drop-target). Its methods let you get information about the drag state of a specific drop target. The specific drop target bound to that monitor is called the monitor's _owner_ below.
10+
`DropTargetMonitor` is an object passed to a collecting function of a [hooks-based](/docs/api/use-drop) or [decorator-based](/docs/api/drop-target). Its methods let you get information about the drag state of a specific drop target. The specific drop target bound to that monitor is called the monitor's _owner_ below.
1111

1212
### Methods
1313

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
path: '/docs/api/dnd-provider'
3+
title: 'DndProvider'
4+
---
5+
6+
_New to React DnD? [Read the overview](/docs/overview) before jumping into the docs._
7+
8+
# DndProvider
9+
10+
The DndProvider component provides React-DnD capabilities to your application. This must be
11+
injected with a backend via the `backend` prop, but it may be injected with a `window` object.
12+
13+
### Usage
14+
15+
```jsx
16+
import { HTML5Backend } from 'react-dnd-html5-backend'
17+
import { DndProvider } from 'react-dnd'
18+
19+
export default class YourApp {
20+
render() {
21+
return (
22+
<DndProvider backend={HTML5Backend}>
23+
/* Your Drag-and-Drop Application */
24+
</DndProvider>
25+
)
26+
}
27+
}
28+
```
29+
30+
### Props
31+
32+
- **`backend`**: Required. A React DnD backend. Unless you're writing a custom one, you probably want to use the [HTML5 backend](/docs/backends/html5) that ships with React DnD.
33+
- **`context`**: Optional. The backend context used to configure the backend. This is dependent on the backend implementation.
34+
- **`options`**: Optional. An options object used to configure the backend. This is dependent on the backend implementation.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
path: '/docs/api/drag-preview-image'
3+
title: 'DragPreviewImage'
4+
---
5+
6+
_New to React DnD? [Read the overview](/docs/overview) before jumping into the docs._
7+
8+
# DragPreviewImage
9+
10+
A Component to render an HTML Image element as a disconnected drag preview.
11+
12+
### Usage
13+
14+
```jsx
15+
import { DragSource, DragPreviewImage } from 'react-dnd'
16+
17+
function DraggableHouse({ connectDragSource, connectDragPreview }) {
18+
return (
19+
<>
20+
<DragPreviewImage src="house_dragged.png" connect={connectDragPreview} />
21+
<div ref={connectDragSource}>🏠</div>
22+
</>
23+
)
24+
}
25+
export default DragSource(
26+
/* ... */
27+
(connect, monitor) => ({
28+
connectDragSource: connect.dragSource(),
29+
connectDragPreview: connect.dragPreview()
30+
})
31+
)
32+
```
33+
34+
### Props
35+
36+
- **`connect`**: Required. The drag preview connector function
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
---
2+
path: '/docs/api/hooks-overview'
3+
title: 'Overview'
4+
---
5+
6+
_New to React DnD? [Read the overview](/docs/overview) before jumping into the docs._
7+
8+
# Using the Hooks API
9+
10+
The hooks-based React DnD API leverages one of the significant newer features of React. Hooks have dramatically impacted how most people write their Reoct components, and are the recommended approach for writing stateful and effectful code within React. Prior to hooks, the React community poured a lot of effort into Higher Order Components and Decorator-based libraries. After hooks were introduced to the React community, there has been a dramatic shift in switching towards approaches and libraries that utilize hooks over decorator-based techniques.
11+
12+
In the overview page, it is pointed out that Drag-and-drop interations are inherently _stateful_. Because of this React DnD has been designed to take advantage of the Flux data pattern and model drag-and-drop state using actions and reducers (independent of React). Hooks are the perfect way to utilize a stateful data source in React. In fact, this is the approach taken by many state-management libraries in React!
13+
14+
There are three primary hooks that are provided to wire your components into React DnD, and a fourth is provided to give you a seam into React DnD (for testing or development purposes)
15+
16+
- [`useDrag`](/docs/api/use-drag)
17+
- [`useDrop`](/docs/api/use-drop)
18+
- [`useDragLayer`](/docs/api/use-drag-layer)
19+
- [`useDragDropManager`](/docs/api/use-drag-drop-manager) (_dev/test hook_)
20+
21+
## Basic Example
22+
23+
To start using hooks, let's make a box draggable.
24+
25+
```jsx
26+
import { useDrag } from 'react-dnd'
27+
28+
function Box() {
29+
const [{ isDragging }, drag, dragPreview] = useDrag({
30+
// "type" is required. It is used by the "accept" specification of drop targets.
31+
item: { type: 'BOX' },
32+
// The collect function utilizes a "monitor" instance (see the Overview for what this is)
33+
// to pull important pieces of state from the DnD system.
34+
collect: (monitor) => ({
35+
isDragging: monitor.isDragging()
36+
})
37+
38+
return (
39+
{/* This is optional. The dragPreview will be attached to the dragSource by default */}
40+
<div ref={dragPreview} style={{ opacity: isDragging ? 0.5 : 1}}>
41+
{/* The drag ref marks this node as being the "pick-up" node */}
42+
<div role="Handle" ref={drag}>
43+
</div>
44+
)
45+
})
46+
}
47+
```
48+
49+
Now, let's make something for this to drag into.
50+
51+
```jsx
52+
function Bucket() {
53+
const [{ canDrop, isOver }, drop] = useDrop({
54+
// The type (or types) to accept - strings or symbols
55+
accept: 'BOX',
56+
// Props to collect
57+
collect: (monitor) => ({
58+
isOver: monitor.isOver(),
59+
canDrop: monitor.canDrop()
60+
})
61+
})
62+
63+
return (
64+
<div
65+
ref={drop}
66+
role={'Dustbin'}
67+
style={{ backgroundColor: isActive ? 'red' : 'white' }}
68+
>
69+
{isActive ? 'Release to drop' : 'Drag a box here'}
70+
</div>
71+
)
72+
}
73+
```
74+
75+
To explore further, read the individual hook API documentation, or check out the [hook-based examples](https://github.com/react-dnd/react-dnd/tree/main/packages/examples-hooks) on GitHub.

packages/docsite/markdown/docs/01 Top Level API/useDrag.md renamed to packages/docsite/markdown/docs/03 Using Hooks/useDrag.md

+11-6
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,21 @@ _New to React DnD? [Read the overview](/docs/overview) before jumping into the d
99

1010
# useDrag
1111

12-
A hook to use the current component as a drag-source.
12+
The `useDrag`hook provides a way to wire your component into the DnD system as a _drag source_. By passing in a specification object into `useDrag`, you declaratively describe the data `item` that will be passed to the DnD system, describe what props to `collect`, and more. The `useDrag` hooks returns a few key items: a set of collected props, and refs that may be attached to _drag source_ and _drag preview_ elements
1313

1414
```jsx
1515
import { useDrag } from 'react-dnd'
1616

1717
function DraggableComponent(props) {
18-
const [collectedProps, drag] = useDrag({
18+
const [collected, drag, dragPreview] = useDrag({
1919
item: { id, type }
2020
})
21-
return <div ref={drag}>...</div>
21+
return collected.isDragging ?
22+
<div ref={dragPreview> : (
23+
<div ref={drag} {...collected}>
24+
...
25+
</div>
26+
)
2227
}
2328
```
2429
@@ -28,9 +33,9 @@ function DraggableComponent(props) {
2833
2934
#### Return Value Array
3035
31-
- **`Index 0`**: An object containing collected properties from the collect function. If no `collect` function is defined, an empty object is returned.
32-
- **`Index 1`**: A connector function for the drag source. This must be attached to the draggable portion of the DOM.
33-
- **`Index 2`**: A connector function for the drag preview. This may be attached to the preview portion of the DOM.
36+
- **`[0] - Collected Props`**: An object containing collected properties from the collect function. If no `collect` function is defined, an empty object is returned.
37+
- **`[1] - DragSource Ref`**: A connector function for the drag source. This must be attached to the draggable portion of the DOM.
38+
- **`[2] - DragPreview Ref`**: A connector function for the drag preview. This may be attached to the preview portion of the DOM.
3439
3540
### Specification Object Members
3641

0 commit comments

Comments
 (0)