In my React web app, I have a number of react-dnd
drop targets:
const DropTarget = () => {
const [{ canDrop, isOver }, drop] = useDrop(() => ({
accept: 'MyDnDItemType',
drop: () => ({ name: 'name of this drop target' })
}))
...
}
and items to drag around and drop in those targets:
const DraggableItem = () => {
const [{ isDragging }, drag] = useDrag({
type: 'MyDnDItemType'
})
...
When a DraggableItem
is dropped into one of the DropTarget
s, I would like to get the drop target name
. The react-dnd
useDrag
docs say in the end(item, monitor)
section:
If [...] the drop target specified a drop result by returning a plain object from its
drop()
method, it will be available asmonitor.getDropResult()
.
However, when I add an end(...)
function in useDrag(...)
and attempt to access the plain object returned, I get a TypeScript compiler error Object is of type 'unknown'.ts(2571)
.
const DraggableItem = () => {
const [{ isDragging }, drag] = useDrag({
end: (item, monitor) => {
const dropResult = monitor.getDropResult();
// console.log('dropResult:', dropResult)
// sample log output:
// dropResult: {dropEffect: 'move', name: 'name of this drop target'}
// PROBLEM: this fails to compile with
// Object is of type 'unknown'.ts(2571)
console.log('name:', dropResult.name)
},
type: 'MyDnDItemType'
})
...
I also tried end: (item, monitor: DragSourceMonitor) ...
and, after reading about this somewhat related issue and delving into the source code, tried to use an explicit type:
type MyType = {
name: string;
};
const DraggableItem = () => {
const [{ isDragging }, drag] = useDrag({
end: (item, monitor: DragSourceMonitor<MyType>) => {
... (as above) ...
but the error persists.
How can I access the plain object returned by getDropResult()
?
Figured it out: Was on the right track, but didn't follow through.
Some quick background info: Until a couple of minutes ago, I knew only very little about TypeScript and, as for generics, only what I remember about C++ templates from ~20 years ago; probably close to nothing. It's not that hard, though:
The DragSourceMonitor
interface takes two type parameters DragObject
and DropResult
, which per default are unknown
:
export interface DragSourceMonitor<DragObject = unknown, DropResult = unknown>
One of the methods on that interface is getDropResult
which takes a single type parameter T
, using DropResult
as default:
getDropResult<T = DropResult>(): T | null
This provides two entry points to fix the compiler error:
Either use own types to pass to DragSourceMonitor
, e.g.
type MyDragObject = {
name: string;
}
type MyDropResult = {
name: string;
}
const DraggableTreeItem = (props: TreeItemProps) => {
const [{ isDragging }, drag] = useDrag({
collect: (monitor: DragSourceMonitor) => ({
isDragging: monitor.isDragging()
}),
...
end: (item, monitor: DragSourceMonitor<MyDragObject, MyDropResult>) => {
...
or pass the type to use only when calling getDropResult(...)
, e.g.
type MyDropResult = {
name: string;
}
const DraggableTreeItem = (props: TreeItemProps) => {
const [{ isDragging }, drag] = useDrag({
...
end: (item, monitor: DragSourceMonitor) => {
const dropResult = monitor.getDropResult<MyDropResult>()
// getDropResult returns `T | null`, so need to test
// against null to fix `Object is possibly 'null'.ts(2531)`
if (dropResult) {
console.log('name:', dropResult.name)
}
...
}
...