reactstrap: Tooltip cannot find ID of a React element rendered next to it
Issue description
- components:
Tooltip
- reactstrap version
5.0.0-alpha.4
- import method
umd
, not full version - react version
16.2.0
- bootstrap version
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
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 20
- Comments: 42 (7 by maintainers)
Commits related to this issue
- Fix broken test Need to mount the component to the document that JSDOM creates so that the dynamic Popover id can be found. See: https://github.com/reactstrap/reactstrap/issues/773#issuecomment-3734... — committed to onaio/kaznet-frontend by moshthepitt 5 years ago
- Fix broken test Need to mount the component to the document that JSDOM creates so that the dynamic Popover id can be found. See: https://github.com/reactstrap/reactstrap/issues/773#issuecomment-3734... — committed to onaio/kaznet-frontend by moshthepitt 5 years ago
- Fix tooltip tests - 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 w... — committed to Kornil/design-react-kit by Kornil 4 years ago
@yidingalan enzyme’s
mount
takes a second parameter;options
. Options has a property,attachTo
which is used to tell it where to mount the component. It’s not enough to add a div to the body, you have to then tell it to mount the component to that div.More information about mount and it’s options: https://github.com/airbnb/enzyme/blob/master/docs/api/mount.md#mountnode-options--reactwrapper
I was having this issue in my tests, which originally was:
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:
I hope this can bring any insights to what you are experiencing.
I had the same problem using react hooks. I solved it with a
useEffect
. My component is aLink
component (a wrapper around<a>
), I share the code in case someone needs: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. If you cache the IDs (created them once in your constructor and reference them from state) you should be able to avoid the issue.
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.
@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
field.subprop-1
. When I removed the.
from the names the tips started working again. Hopefully this helpsThe 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. This is needed because the reactstrap code looks in the document for the target and if the target is mounted outside of the document it cannot find it. If you would like to investigate a better way for reactstrap to locate the target or something to help the tests not need to mount the application/component in the document, go for it.
The issue is that it is trying to find your target element before it is rendered to the DOM. This happens when the tooltip is open when it is initialized. Not too sure how to address this, but the workaround it is either toggle it after it initializes or provide a ref or function to get a ref/DOM node to
target
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.
Same issue here and none of the solutions above have been working so far =( We are also having:
TypeError: Cannot read property 'removeEventListener' of undefined
Thanks all. FYI that this is still an issue.
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.
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.
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
Yeah, the target is a css selector, so the
.
indicates classname. We may want to initially search for just id (via getElementById) and fallback to css selector.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:
My current solution is by using generated ID, but I make sure that it only created once by using zero-dependencies
useMemo
, thus successfully generated a random ID that will not change in subsequent re-render. Hope this helps someone. Any suggestions if this solution has a downside are welcomed.Code:
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).
@soywod , that’s a very good solution! Using reference and to make sure that Tooltip will render after reference is applied.
Hi everyone,
just wanted to drop in quickly, and say thanks for all of your work on
reactstrap
, and specifically on this issue! I would like to confirm that this still occurs with5.0.0-beta
; as for @Cretezy, in-browser production code works fine but CI builds viajsDom
fail for me on the most basic tests; their workaround fixes things successfully.I’ve been able to isolate the issue to
UncontrolledTooltip
components (I think) added to the page after the initial render, would be happy to investigate further if this is useful – please let me know.All the best, and thanks again to you all for this awesome library, it makes my work easier every day!
-Felix