React beautiful DND - I get "Unable to find draggable with id: 1" - javascript
In the code below the UI renders two "Column" components and each column contains two draggable elements called "Tasks". When the user drags a "Task" between columns the code works - up to a point. When the user continuously drags the task components around eventually they will stop dragging and the user gets an error that says:
Unable to find draggable with id: X
I don't know why this happens nor how to fix it.
Note: I am assuming the way the library works is when you drag elements you need to reorder and update your state in the onDragEnd function.
Here is my code:
app.js
import React,{useState} from 'react';
import {DragDropContext} from 'react-beautiful-dnd';
import helper from './helper_functions'
import Column from './Components/Column';
function App() {
let initialState = [
{
groupName:"Today",
tasks:[
{id:"1", title:"Test-1"},
{id:"2", title:"Test-2"}
]
},
{
groupName:"Tomorrow",
tasks:[
{id:"3", title:"Test-3"},
{id:"4", title:"Test-4"}
]
},
]
const [taskList, setTasks] = useState(initialState)
function onDragEnd(val){
let result = helper.reorder(val.source,val.destination,taskList);
setTasks(result)
}
return (
<DragDropContext onDragEnd={onDragEnd}>
<Column droppableId="Today" list= {taskList[0].tasks} type="TASK"/>
<Column droppableId ="Tomorrow" list = {taskList[1].tasks} type="TASK"/>
<div> context hello world </div>
</DragDropContext>
);
}
export default App;
src/helper_functions
export default {
reorder:function(source,destination,taskDataArr){
let taskData = [...taskDataArr]
// //_____________________________________________________________Source data
let sourceGroupIndex = taskData.findIndex((val, index) => { // iterate and find "Today" (or other) index in list data
return val.groupName === source.droppableId
});
let draggedTask = taskData[sourceGroupIndex].tasks.find((val, index) => { // Get specific task object based on index
return source.index === index
}); // dragged object
let sourceListCopyWithElementRemoved = taskData[sourceGroupIndex].tasks.filter((val, index) => {
return index !== source.index // removes dragged element from array
});
// //__________________________________________________________________Destination data
let destinationGroupIndex = taskData.findIndex((val, index) => { // iterate and find "Tomorrow" (or other) index in list data
return val.groupName === destination.droppableId
});
taskData[destinationGroupIndex].tasks.splice(destination.index, 0, draggedTask); // insert dragged item to new place
taskData[sourceGroupIndex].tasks = sourceListCopyWithElementRemoved
return taskData
}
}
src/Components/Column
import React from 'react';
import {Droppable} from 'react-beautiful-dnd';
import Task from "../../Components/Task"
function Column(props){
const { classes, droppableId, list, type} = props;
let style = {
backgroundColor:"orange",
height:"300px",
width:"400px",
margin:"100px"
}
console.log(list)
return (
<Droppable droppableId = {droppableId} type={type}>
{provided => (
<div {...provided.droppableProps} ref={provided.innerRef} style={style}>
<h2>{droppableId}</h2>
{list.map((val,index)=>{
return <Task id={val.id} key={index} index={index} title={val.title}/>
})}
{provided.placeholder}
</div>
)
}
</Droppable>
)
}
export default Column
src/Components/Task
import React from 'react';
import {Draggable} from 'react-beautiful-dnd';
function Task(props){
const { classes, id, index,title } = props;
let style = {
backgroundColor:"red",
}
return (
<Draggable draggableId ={id} index={index} type="TASK">
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<h4 style={style}>{title}</h4>
</div>
)}
</Draggable>
)
}
export default Task
There are a few issues with your code:
Error Unable to find draggable with id: X
In the Column component you used index as a key for the tasks. I think this is what causes this error.
Using the task id as key, in Column, makes this error go away.
reorder has some issues:
it removes a task when dropped in the same column
raises an error when a task is dropped outside the columns
I had a bit of fun with you code and tried another way to reorder. This way of reordering might come in handy if you ever add a more columns - there is still room for improvements.
Hope it helps!
If you are getting this error on React 18
Unable to find draggable with id:
Try removing StrictMode
Not for this case, but
For every one - check provided.draggableProps not provided.dropableProps
<Draggable draggableId ={id} index={index} type="TASK">
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<h4 style={style}>{title}</h4>
</div>
)}
</Draggable>
RBD try find node by provided.draggableProps. Missing this props got error: Unable to find draggable with id: X
I had a similar issue, I was mapping through multiple draggable and had missed the key prop,
Adding the key to Draggable fixed the issue for me
{tasks.map((t, index) => (
<Draggable
draggableId={t._id}
index={index}
key={t._id} >
....
</Draggable>
}
For Error:
Unable to find draggable with id: X
Remove Strict mode in React js.
If you are using Next.js you can find the strict mode in next.config.js file
For Error:
Invariant failed: Draggable[id: 1]: Unable to find drag handle do the following
{process.browser && (
<section>
<DragDropContext onDragEnd={onDragEnd}>
//Your code with droppable and draggable
</DragDropContext>
</section>
)}
Related issue
I ran into the same issue however the following steps helps me
Step 1:
draggableId should be string, not an integer
as per https://github.com/atlassian/react-beautiful-dnd/issues/1512
Step 2:
if you are coming from EggHead tutorial, this might have missed.
Try to add a key property as mentioned in the top answer
<Draggable key={item.id} draggableId={item.id.toString()} index={index} >
Try converting the integer value to a string. It'll solve the issue as it solved mine
{tasks.map((t, index) => (
<Draggable
draggableId={t._id.toString()}
index={index}
key={t._id} >
....
</Draggable>
}
There is a YouTube lesson on react-beautiful-dnd. He posted his
lesson progress in the Code Sandbox enter link description here
You can open it and make sure. But I have on "react": "^18.1.0" does
not work and gives an error
Unable to find draggable with id:X
And on older versions of react everything also works without
errors.Try for example another version of react"^17.0.2"ю It worked
for me
Also remember, when installing the react-beautiful-dnd library, were there any dependency conflicts? Maybe you have not established all the connections and because of this it does not work correctly. In any case, as a test, I suggest downloading the code from the lesson and checking in my project whether it works or not. If it works, it means an error in the code, if not, then react-beautiful-dvd does not work correctly
I hope this will help someone. If you are experiencing this even your code is correct. Please check your react version. In react version 18.0, dnd will show unable to find draggable with id warning. Current solution to downgrade to 17 and it works flawlessly. I searched for the solution like remove strict mode, using dynamic import and other things but currently it's related to react 18.
Post it here, since this is the top link the Google for the same error.
It might be the case, that you do not render an element with the provided.dragHandleProps
<div>
{someCondition && (
<div>
<DragIndicatorIcon {...provided.dragHandleProps}/>
</div>
)}
</div>
In this case move {...provided.dragHandleProps} outside the condition
<div {...provided.dragHandleProps}>
{someCondition && (
<div>
<DragIndicatorIcon {...provided.dragHandleProps}/>
</div>
)}
</div>
Found this solution here
Resolved this issue by converting draggableId into string
Removing <React.StrictMode> fixed it for me.
It works with react-beautiful-dnd and React 18 in StrictMode.
https://github.com/atlassian/react-beautiful-dnd/issues/2399#issuecomment-1175638194
Related
React JS Frontend - Key Error When Rendering List
I am getting the following error when rendering a list using ReactJS react-jsx-dev-runtime.development.js:87 Warning: Each child in a list should have a unique "key" prop. Check the render method of `DriverList`. See https://reactjs.org/link/warning-keys for more information. at DriverList (http://localhost:3000/static/js/bundle.js:720:13) at div at Drivers (http://localhost:3000/static/js/bundle.js:1401:75) at Routes (http://localhost:3000/static/js/bundle.js:47364:5) at Router (http://localhost:3000/static/js/bundle.js:47297:15) at BrowserRouter (http://localhost:3000/static/js/bundle.js:46106:5) at App So far I have tried to unsuccessfully resolve it by adding the key value to both the Driver, a higher level div than the Driver object that doesn't need to be there, adding a key to the a React Fragment wrapped around the Driver object. I have tried adding it to each of them individually as well as multiple parts. I have also tried adding the key to the li item within the actual Driver object file that populates the DriverList. I still keep getting the error and I am thinking I am making a stupid mistake somewhere along the code that someone can point out and laugh at me for. import React from "react" import Driver from "./Driver" import Card from "../../shared/components/interface/Card" import "./DriverList.css" const DriverList = (props) => { if (props.items.length === 0) { return ( <Card key='none'> <h2>No drivers found.</h2> </Card> ) } else { // Render the List return ( <ul className='driver-list'> {props.items.map((driver) => { return ( <React.Fragment key={driver.id}> <Driver key={driver.id} id={driver.id} permitno={driver.permitno} fname={driver.fname} lname={driver.lname} mailaddr1={driver.mailaddr1} mailaddr2={driver.mailaddr2} dateofbirth={driver.dateofbirth} driversex={driver.driversex} licenseidno={driver.licenseidno} dvrclass={driver.dvrclass} dvrpermit={driver.dvrpermit} endorsemnt={driver.endorsemnt} restricts={driver.restricts} expdate={driver.expdate} cmpldate={driver.cmpldate} numpoints={driver.numpoints} suspended={driver.suspended} driver_status={driver.driver_status} /> </React.Fragment> ) })} </ul> ) } } export default DriverList
try: props.items.map((driver, idx) => { return ( <Driver key={idx} and see if it helps. And if it does it means that driver.id repeats somewhere
ReactJs page renders fine when using .map of objects but if use index of array runs on first save but crashes on reload
I am creating a flashcard app, already have a page where I use .map() to render all items. Now I created another page so I can individually show one card at a time, so instead of using .map I used items[x] so I can increment by one with a onClick button. When I first save the file the live update renders fine as predicted, yet if I reload the page it crashes saying that it cannot read the word property of undefined. Now after doing some debugging I found that If I console log the array at first it shows up as zero/0 then prints out again with the info, even though I call the useSelector function to gather the info from the state before trying to access the data. Now this happens no matter if I use the .map() function but for some reason the .map() function does not crash and render fine as expected. import React from "react"; import FlippableFlashcard from "../FlippableFlashcards/FlippableCard/FlippableCard.jsx"; import { Button, Container, Box } from "#mui/material"; import ArrowRightIcon from "#mui/icons-material/ArrowRight"; import ArrowLeftIcon from "#mui/icons-material/ArrowLeft"; import { useSelector } from "react-redux"; import useStyles from "./styles.js"; const Practice = () => { const classes = useStyles(); const items = useSelector((state) => state.posts); const handleAdd = () => {}; return ( <> <Container className={classes.centerDiv}> <FlippableFlashcard item={console.log(items[0])} /> {/*//This fails, but this does not <FlippableFlashcard item={items.map((item) => console.log(item))} />*/} </Container> <Box textAlign="center" className={classes.ButtonBar}> <Button onClick={handleAdd}> <ArrowLeftIcon /> </Button> <Button> <ArrowRightIcon /> </Button> </Box> </> ); }; export default Practice; specifically this <FlippableFlashcard item={console.log(items[0])} /> Error: FlippableCard.jsx:20 Uncaught TypeError: Cannot read properties of undefined (reading 'word') So I know it is because of that first console log being 0/null/undefined. Tried doing { items && items[0] } as I saw some saying this would work but did not.
Thanks to Akshay Mathur, I just simply did, works great, basically checks if data exists if not then simply console logs fetching, on the second auto reload it will render. {items.length ? ( <FlippableFlashcard item={items[x]} key={items[x]._id} /> ) : ( console.log("fetching data") )}
Do something like this: {items.length > 0 && items.map((item,index)=>(<FlippableFlashcard item={item} key={index} />)}
TypeError: Cannot read properties of undefined (reading 'style')
Been stuck on debugging this for quite a while. I'm trying to have a group of items change onClick but with the use of transform but 'style' is undefined. I've also included the Card component functions. Help would be greatly appreciated import React,{useRef} from 'react'; import { Card } from '../components'; import { CardItemContainer } from './card-item'; export function CardContainer() { const listRef=useRef() const handleClick=(direction)=> { if(direction==="left") { listRef.current.style.transform=`translate(230)` } } return( <Card> <Card.ListTitle> Continue to watch</Card.ListTitle> <Card.Wrapper > <Card.ArrowSliderLeft onClick={()=>handleClick('left')}/> <Card.List ref={listRef}> <CardItemContainer index={0}/> <CardItemContainer index={1}/> <CardItemContainer index={2}/> <CardItemContainer index={3}/> <CardItemContainer index={4}/> <CardItemContainer index={5}/> <CardItemContainer index={6}/> </Card.List> <Card.ArrowSliderRight onClick={() => handleClick("right")}/> </Card.Wrapper> </Card> ) } Card Components import {ArrowBackIosOutlined,ArrowForwardIosOutlined} from "#material-ui/icons"; import React, {} from 'react'; import { Container, List, ListTitle, Wrapper, ArrowSliderLeft, ArrowSliderRight } from './styles/card'; export default function Card({ children, ...restProps }) { return <Container {...restProps}>{children}</Container> } Card.ListTitle=function CardListTitle({children,...restProps}) { return <ListTitle{...restProps}> {children} </ListTitle> } Card.Wrapper=function CardWrapper({children,...restProps}) { return <Wrapper{...restProps} > {children} </Wrapper> } Card.List=function CardList({children,...restProps}) { return <List{...restProps} >{children}</List> } Card.ArrowSliderLeft = function HeaderArrowBackIosOutlinedSymbol({...restProps }) { return <ArrowSliderLeft {...restProps }> {/*id allows me to style the icon directly */} <ArrowBackIosOutlined id="sliderLeft"/> </ArrowSliderLeft> } Card.ArrowSliderRight = function HeaderArrowForwardIosOutlinedSymbol({...restProps}) { return ( <ArrowSliderRight {...restProps}> <ArrowForwardIosOutlined id="sliderRight"/> </ArrowSliderRight> ); }; Ignore: Been stuck on debugging this for quite a while. I'm trying to have a group of items change onClick but with the use of transform but 'style' is undefined. I've also included the Card component functions. Help would be greatly appreciated
Function components like CardList don't have a ref property, only class components or DOM elements do. You haven't posted List component's implementation, but let's assume it has a <ul> tag, and that is what you eventually need to manipulate its .style.transform CardList >>> List >> ul (this is the element you need to pass the ref) To pass the listRef all the way to ul from CardList you need to use the forwardRef technique. Card.List=React.forwardRef(function CardList (props,ref) { const {children,...restProps} = props return <List{...restProps} ref={ref} >{children}</List> }) the List component itself : const List = React.forwardRef(function (props,ref) { return <ul ref={ref}> ... the implementation of your List Now you can pass listRef in here and it goes down the chain: <Card.List ref={listRef}> Side Note: taking from Drew Reese's comment on this answer, since CardList is just transfering the same props from a parent component to List, you can simply assign List to Card.List, then only one step of ref forwarding would be enough: Card.List = List // CardList component isn't used anymore. The same thing could work for Card.ListTitle and Card.Wrapper: Card.ListTitle=ListTitle Card.Wrapper=Wrapper
I too have just faced this same issue, and have tried to get my code working again. Checking similarity between your given code and my erroneous code snippet helped me fix the error. Strangely, I have faced this error with a JSX multi-line comment in place after my element (MUI <Snackbar> element, in my case). Error(s): My code snippet looked something like: <Snackbar open={snackbarOpen} autoHideDuration={5000} onClose={()=>setSnackbar(false)} > {/* My Comment Here */} <>...</> </Snackbar> Quite similar place of JSX comment as your Card Component Card.ArrowSliderLeft = function ... return <ArrowSliderLeft {...restProps }> {/*id allows me to style the icon directly */} <ArrowBackIosOutlined ... /> </ArrowSliderLeft> Removing just the comment part {/* */} immediately following an opening tag worked for me. So, try removing your JSX comment or placing it elsewhere,and see if it helps. Sharing it here just for my and others future reference. :)
React key prop within wrapper
While looking through our code base, I found code that looks a bit like this: const Carousel = ({ items }) => { return ( <CarouselOuter> {items.map((item) => ( <CarouselItemWrapper> <CarouselItem key={item.key}> ... </CarouselItem> </CarouselItemWrapper> )} </CarouselOuter> ); } Notice that the key prop is on CarouselItem, not CarouselItemWrapper, the component that's directly returned from items.map. This seems to work fine, and there are no warnings in the console, but it runs counter to every example I've seen using map in React. I want know if there's a good argument (specifically in regards to performance) for rearranging the code with the key as shown below, or if this is just a stylistic choice: const Carousel = ({ items }) => { return ( <CarouselOuter> {items.map((item) => ( <CarouselItemWrapper key={item.key}> <CarouselItem> ... </CarouselItem> </CarouselItemWrapper> )} </CarouselOuter> ); } Side note: CarouselOuter, CarouselItem, and CarouselItemWrapper are all styled-components, but I doubt that's relevant.
How can I return multiple lines JSX in another return statement in React?
A single line works fine: render: function () { return ( {[1,2,3].map(function (n) { return <p>{n}</p> }} ); } But not for multiple lines: render: function () { return ( {[1,2,3].map(function (n) { return ( <h3>Item {n}</h3> <p>Description {n}</p> ) }} ); }
Try to think of the tags as function calls (see the documentation). Then the first one becomes: {[1,2,3].map(function (n) { return React.DOM.p(...); })} And the second one: {[1,2,3].map(function (n) { return ( React.DOM.h3(...) React.DOM.p(...) ) })} It should now be clear that the second snippet doesn't really make sense (you can't return more than one value in JavaScript). You have to either wrap it in another element (most likely what you'd want, that way you can also provide a valid key property), or you can use something like this: {[1,2,3].map(function (n) { return ([ React.DOM.h3(...), React.DOM.p(...) ]); })} With JSX syntactic sugar: {[1,2,3].map(function (n) { return ([ <h3></h3>, // note the comma <p></p> ]); })} You don't need to flatten the resulting array. React will do that for you. See the following fiddle http://jsfiddle.net/mEB2V/1/. Again: Wrapping the two elements into a div/section will most likely be better long term.
It seems Jan Olaf Krems's answer about returning an array no longer applies (maybe since React ~0.9, as #dogmatic69 wrote in a comment). The documentation says you need to return a single node: Maximum Number of JSX Root Nodes Currently, in a component's render, you can only return one node; if you have, say, a list of divs to return, you must wrap your components within a div, span or any other component. Don't forget that JSX compiles into regular JS; returning two functions doesn't really make syntactic sense. Likewise, don't put more than one child in a ternary. In many cases you can simply wrap things in a <div> or a <span>. In my case, I wanted to return multiple <tr>s. I wrapped them in a <tbody> – a table is allowed to have multiple bodies. As of React 16.0, returning an array is apparently allowed again, as long as each element has a key: New render return types: fragments and strings React 16.2 lets you surround a list of elements with <Fragment>…</Fragment> or even <>…</>, if you prefer that to an array: https://reactjs.org/docs/fragments.html
From React v16.0.0 onwards, it is possible to return multiple elements by wrapping them within an Array: render() { return ( {[1,2,3].map(function (n) { return [ <h3>Item {n}</h3>. <p>Description {n}</p> ] }} ); } Also from React v16.2.0, a new feature called React Fragments is introduced which you can use to wrap multiple elements: render() { return ( {[1,2,3].map(function (n, index) { return ( <React.Fragment key={index}> <h3>Item {n}</h3> <p>Description {n}</p> </React.Fragment> ) }} ); } As per the documentation: A common pattern in React is for a component to return multiple elements. Fragments let you group a list of children without adding extra nodes to the DOM. Fragments declared with the explicit <React.Fragment> syntax may have keys. A use case for this is mapping a collection to an array of fragments — for example, to create a description list: function Glossary(props) { return ( <dl> {props.items.map(item => ( // Without the `key`, React will fire a key warning <React.Fragment key={item.id}> <dt>{item.term}</dt> <dd>{item.description}</dd> </React.Fragment> ))} </dl> ); } key is the only attribute that can be passed to Fragment. In the future, we may add support for additional attributes, such as event handlers.
Also, you might want to return several list items in some helper function inside a React component. Just return an array of HTML nodes with the key attribute: import React, { Component } from 'react' class YourComponent extends Component { // ... render() { return ( <ul> {this.renderListItems()} </ul> ) } renderListItems() { return [ <li key={1}>Link1</li>, <li key={2}>Link2</li>, <li key={3} className="active">Active item</li>, ] } }
Updated Use React Fragment. It's simple. Link to fragment documentation. render() { return ( <> {[1,2,3].map((value) => <div>{value}</div>)} </> ); } Old answer - obsolete With React > 16 you can use react-composite. import { Composite } from 'react-composite'; // ... {[1,2,3].map((n) => ( <Composite> <h2>Title {n}</h2> <p>Description {n}</p> </Composite> ))}; Of course, react-composite has to be installed. npm install react-composite --save
It is simple by React fragment <></> and React.Fragment: return ( <> {[1, 2, 3].map( (n, index): ReactElement => ( <React.Fragment key={index}> <h3>Item {n}</h3> <p>Description {n}</p> </React.Fragment> ), )} </> );
You can use createFragment here. See Keyed Fragments. import createFragment from 'react-addons-create-fragment'; ... {[1,2,3].map((n) => createFragment({ h: <h3>...</h3>, p: <p>...</p> }) )} (I am using ES6 and JSX syntax here.) You first have to add the react-addons-create-fragment package: npm install --save react-addons-create-fragment The advantage over Jan Olaf Krems's solution: React does not complain about the missing key.
This happens when you are not on the current project folder or the folder you are currently on contains more than one project, you probably will get this error. I had a similar error and once switched to a current project folder and run, the issue is gone.