New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tooltip cannot find ID of a React element rendered next to it #773
Comments
I've managed to reduce the problem even further: it seems that the problem is much simpler than I first thought. New codepen here has the issue. |
The issue is that it is trying to find your target element before it is rendered to the DOM. |
I was having this issue in my tests, which originally was: import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
describe("App", () => {
test("renders without crashing", () => {
const div = document.createElement("div");
ReactDOM.render(<App />, div);
});
}); This would give the error discussed. To solve it (at least for my tests), I had to add the div to the body, since the tooltip tries to query the document. This was the fix: import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
describe("App", () => {
test("renders without crashing", () => {
const div = document.createElement("div");
document.body.appendChild(div);
ReactDOM.render(<App />, div);
});
}); I hope this can bring any insights to what you are experiencing. |
I tried creating the element separately, to try to referencing the target directly (am I just misunderstanding? The docs don't have an example), but the error suggests that it's still looking for an element attached to the DOM at render-time: https://codepen.io/anon/pen/WdBBMW |
@k-funk I waited for a EDIT ... but this approach doesn't seem to play well with my DOM router and back-buttoning history, perhaps due to a component lifecycle issue, |
Hi everyone, just wanted to drop in quickly, and say thanks for all of your work on I've been able to isolate the issue to All the best, and thanks again to you all for this awesome library, it makes my work easier every day! -Felix |
The JSDOM issue is because you are not mounting your application/component to the document during testing. Simply mount your application/component to the document JSDOM creates and it will work. |
Ah, I see, thanks! So that's how @Cretezy's workaround functions. That definitely makes sense. In that case, I think this part of the issue might be considered fixed. Thanks a lot for your super-quick response, and again, thanks for your work on |
@TheSharpieOne Seeing this issue on UncontrolledTooltip at render time where the isOpen prop should be false (there is no way for the tips to be hovered before rendering). FOLLOW UP EDIT So, in my case, it turned out that I had some dynamic complex id names like |
Yeah, the target is a css selector, so the |
Hey @TheSharpieOne , I tried this but it still throws the same error
Any idea why? |
@yidingalan enzyme's it('test', () => {
const div = document.createElement('div');
document.body.appendChild(div);
const wrapper = mount(<Component {...props} />, { attachTo: div });
}); More information about mount and it's options: https://github.com/airbnb/enzyme/blob/master/docs/api/mount.md#mountnode-options--reactwrapper |
I have the same problem. I am using tooltip for a button. At first, everything seems ok but when I hover over the button the error is being thrown: Uncaught Error: The target 'tooltip-cftoce14f' could not be identified in the dom, tip: check spelling. Here is my code for the tooltip:
Version details:
Am I doing something wrong? |
You are generating random ID values each render. So when you open the tooltip, it will create a new ID and when the tooltip tries to find an element with the new ID, it will not have been put into the DOM yet. |
@TheSharpieOne thanks a lot it worked! Completly missed this one 😄 |
I have the same problem as @prodevelsol had 2 days ago but I can't figure it out... 😞 Can you help me find what is wrong in my code? It returns this error message :
Where 'rfmygrx07' is the random id
Note : As you can notice, my project integrates https://github.com/kriasoft/isomorphic-style-loader so classes are decorated, but not IDs, so i don't think it is the source of my problems (but I prefer to notice, just in case...) Thanks ! 😃 |
@Thoum I think this can be caused by the fact that you put the id on the root level component so the component is being re-rendered and the id is not in the state. You can try to assign it to the Badge component instead.
is it the badge that has to be tooltip-ed? |
@prodevelsol :
Full code with modifications operated :
Thank you for your help ! 😃 |
I removed isomorphic-style-loader (and the classNames with references to the imported styles) and replaced Badge with reactstrap's badge (just to get it to render) and it works fine: Can you inspect and confirm that the ID on the rendered element matches what the error is saying cannot be found? I see some stuff in that isomorphic-style-loader project which manipulates IDs, but I am not sure if that is the case. |
Yes i can confirm the ID is correct. |
The issue is the way React handles parameters for IDs with strings and ints. You can't use an numeric starting value as the ID. (Hence why your Math.randoms() will break and your test values work) It must start with a string. We should add a note to this to the documentation portion for tooltips. /cc @TheSharpieOne |
The rules of HTML still apply, but I suppose we can add a note to explicitly point it out. |
Need to mount the component to the document that JSDOM creates so that the dynamic Popover id can be found. See: reactstrap/reactstrap#773 (comment)
Need to mount the component to the document that JSDOM creates so that the dynamic Popover id can be found. See: reactstrap/reactstrap#773 (comment)
I'm having an issue that I think is similar to this. In at least two places, the code checks for the target during render: (note: commit ID used is just what the current master is, not related to issue) This means that a component that renders the target and the tooltip (UncontrolledTooltip in my case) will only work on the subsequent render. I hit this issue in the following scenario:
I fixed my issue by adding a key={row.id} to the UncontrolledTooltip instance. This prevented react from re-using the component when the basis for the target id changed. I wrote a crude test where I modified reactstrap and moved the getTarget calls into componentDidUpdate and it seemed to also fix the issue. If there is interest, I can clean up my code a little and submit a PR. |
reproduction: https://codesandbox.io/s/144zyk1zjq Modifying reactstrap to get the target in the appropriate lifecycle methods appears to fix this. EDIT: a workaround/fix is to add key={counterId} to the UncontrolledTooltip to prevent react from re-using the element when the id changes. |
I had the same problem using react hooks. I solved it with a import React, {Fragment, useEffect, useRef, useState} from 'react'
import classNames from 'classnames'
import Tooltip from 'reactstrap/lib/Tooltip'
export default function({className, to, tooltip, onClick, children}) {
const linkRef = useRef()
const [ready, setReady] = useState(false)
const [open, setOpen] = useState(false)
function handleClick(event) {
event.preventDefault()
onClick(event)
}
function toggle() {
setOpen(!open)
}
useEffect(() => {
if (linkRef.current) {
setReady(true)
}
}, [linkRef.current])
return (
<Fragment>
<a
className={classNames(className)}
ref={linkRef}
target="_blank"
rel="noopener noreferrer"
href={to || '#'}
{...(onClick ? {onClick: handleClick} : {})}
>
{children}
</a>
{tooltip && ready && (
<Tooltip
placement="bottom"
isOpen={open}
target={linkRef.current}
toggle={toggle}
>
{tooltip}
</Tooltip>
)}
</Fragment>
)
} |
@soywod , that's a very good solution! Using reference and to make sure that Tooltip will render after reference is applied. |
I had the same problem and decided to wrap @soywod's solution in a component I can reference.
And here is the component that does the wiring up of target, and uses the Ref trick to work around the render order.
|
Key workaround suggested by @pmacmillan works for me. The issue seems to happen if id is generated dynamically, and then the component is re-rendered (so id will change upon re-rendering). |
@TheSharpieOne What if you're using react-test-renderer instead of enzyme's mount? How would you address the issue? Actually I used @soywod 's solution for it. Putting a ref to the target and checking if it's ready, but I don't think it's a good solution because you need to modify the code just to make the test work. This is only happening for me on snapshot testing and not in production. |
- reactstrap UncontrolledTooltip searches the document object for ids at first render, by adding invisible divs with the required ids th tests will pass and on render the tooltips will be correctly applied to the tests' correct buttons reactstrap/reactstrap#773 (comment) - Update snaps
My current solution is by using generated ID, but I make sure that it only created once by using zero-dependencies Code:
|
@AsadSaleh I would use a UUID instead of generating a random number manually. Also, I think a |
|
Yes sorry, I wasn't clear enough. The @AsadSaleh solution looks good, but I would use a UUID instead. Personally I would not use this method, I prefer to keep a tooltip ref as I shared in my previous comment. |
Worth mentioning that the same issue exists with popovers and continues to be a problem that we have to work around in hacky ways (for both tooltips and popovers). The useRef solution in this thread, which we've used until recently has its own issues and introduces console warnings - in addition to a bunch of confusing code to our components that exists entirely to accommodate this quirk in tests. |
Thanks all. FYI that this is still an issue. |
Same issue here and none of the solutions above have been working so far =( |
You can still use @TheSharpieOne solution for the meantime: const btnRef = React.useRef(null);
const [ready, setReady] = React.useState(false);
React.useEffect(() => {
if (btnRef.current) {
setReady(true);
}
}, [btnRef]);
<button
ref={btnRef}
type="button"
id={id}
aria-describedby="tooltip-id"
>
Show tooltip
</button>
{ready && <Tooltip id="tooltip-id">some text</Tooltip>} |
Unfortunately we are using an ancient version of React without hooks 😞 , I will have to find a way to do without them. |
React hooks are just a shortcut for what react already does. It's worth reading through Using the State Hook and Using the Effect Hook. Using classes, this would be something like:
|
@k-funk hey, thanks a bunch. I needed to make a little fix to the
|
Dammit some of my tests are still failing, I think for the time being I will have to remove it. |
This worked perfectly when using
|
Issue description
Tooltip
5.0.0-alpha.4
umd
, not full version16.2.0
4.0.0-beta.3
What is happening?
Sometimes, when I render the attached code, I get this error:
The target <target id> could not be identified in the dom, tip: check spelling
.However, I can't get the codepen to reproduce this, but it consistently happens in my larger application base, which uses the same data flow.The bug occurs when the button in the codepen is pressed,but perhaps React functions differently in my case. I made a small change in the codepen, the bug is now reproduceable.The full application is here: https://github.com/kenzierocks/OurTube/blob/master/client/js/navbar.tsx#L78
I can help set this up to run if needed, but it's still in the middle of initial development
What should be happening?
It should render my custom tooltip properly, with no errors.
Code
https://codepen.io/anon/pen/xpWJzN
The text was updated successfully, but these errors were encountered: