-
Notifications
You must be signed in to change notification settings - Fork 47.1k
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
findDOMNode deprecation #14357
Comments
styled-components used findDOMNode as a debug assist to verify if your wrapper component does eventually forward the className to a DOM node. However, as is the case with all of these uses of findDOMNode, it'll only work correctly if only one DOM Element is eventually rendered, or if the appropriate element is the first one rendered. Any fragment siblings are completely invisible to it. This is why it's deprecated. |
As I commented in styled-components/styled-components#2154 (comment), let's track this issue to enumerate use cases that are addressed by |
I have a project - visual editor for composing layouts with React components ui library by dragging components with mouse. This React components are developed by different team so we don't have a direct access to modify their codebase and for now passing refs to that components is not supported. And we can use different React UI libraries by different developer because with In our project codebase, we wrap each of that components with several HOCs. Each HOC contains some code with findDOMNode - for adding event listeners like click/mousemove or for using So |
About
Details of usage: |
A use case that i think of, is when you want to perform some logic on the underline This is the relevant logic that depends on the ref:
With
The alternative (hooks aside) is to expose the ref callback to the user (via render prop for example):
And the user will attach it to what ever
This will work but the API is kind of awkward, plus we shift the responsibility for dealing with refs to the end user (sort of implementation details). A more critical problem with this API is that it may conflict with other components that are using this approach. Consider a second library like
If a user wants to use both libraries, he/she can't use both callbacks on the same element (again, hooks aside):
I made a running example of the 2 approaches A ref on a fragment can solve these issues as the library can render a "transparent" element yet keep a ref without the end user has to know or worry about it. |
This use case is similar to the use case of The delaying is done, I believe, with cloneElement; but the |
@Fer0x Thanks for explaining the |
I'd say this api is quite nice. I actually would pass ref to tooltip via props which is the easiest way to reuse this ref in another component or hook. |
in some case, if needs to get postion of a component which is from a third lib. I think that |
@Kovensky thanks, I will look into this |
I have a use case where I've wrapped the excellent @bvaughn 's react-window components with my own in order to force repainting on mousewheel scroll. To do this I do the following: render() {
const {props: {syncScroll, ...props}} = this
return <FixedSizeList {...props} ref={this.setListRef} />
}
scrollNode: HTMLDivElement
unregisterScrollEvent = () => {
this.scrollNode && this.scrollNode.removeEventListener("wheel", this.onWheel)
this.scrollNode = null
}
list: FixedSizeList
setListRef = (list: FixedSizeList) => {
this.list = list
if (this.props.syncScroll) {
if (!list) {
this.unregisterScrollEvent()
}
else {
this.scrollNode = ReactDOM.findDOMNode(list) as HTMLDivElement
this.scrollNode.addEventListener("wheel", this.onWheel)
}
}
} How would you suggest I approach this instead? |
I have a use case that the tooltip didn't properly be tethered to menu item inside dropdown that is tethered to a dropdown button. We used Tether and plan to move to Popper but it is not clear Tether is the cause. We found that we can use MutationObserver DOM API to detect DOM mutation and force Tether's global position handler to run and solve this problem. However, we want the MutationObserver to observe a smaller subtree of DOM below a "node", instead of the whole DOM. To do this, we will need to call observe(node) and we need to use findDOMNode to get the node. |
I have a problem that I have so far not been able to solve using refs, but can solve very nicely with I have a fairly complex custom library, but for brevity it can be reduced to the following: import React from "react";
export default function App() {
return (
<Module styles={{ background: 'lightgrey' }}>
<Module styles={{ color: 'red' }}>Some div</Module>
<Module styles={{ color: 'blue' }}>Another div</Module>
</Module>
);
}
class Module extends React.Component {
constructor(props) {
super(props);
this.REF = React.createRef();
this.TAG = props.as || 'div';
}
componentDidMount() {
this.paint(this.REF.current, this.props.styles);
}
// in reality this.paint() does much more than re-construct the
// input styles object as shown here
paint(node, styles = {}) {
for (const [prop, val] of Object.entries(styles)) {
node.style[prop] = val;
}
}
render() {
return (
<this.TAG ref={this.REF}>{this.props.children}</this.TAG>
);
}
} Essentially, within So far, this works fine using refs. The issue occurs when attempting to pass other React Components to the In my case, I am trying to pass the Components from Pure React Carousel to my custom ...
import { CarouselProvider, Slider, Slide } from 'pure-react-carousel';
import 'pure-react-carousel/dist/react-carousel.es.css';
export default function App() {
return (
<CarouselProvider naturalSlideWidth={4} naturalSlideHeight={4} totalSlides={2}>
<Module as={Slider} styles={{ background: 'lightgrey' }}>
<Module as={Slide} styles={{ color: 'red' }}>Slide 1</Module>
<Module as={Slide} styles={{ color: 'blue' }}>Slide 2</Module>
</Module>
</CarouselProvider>
);
}
... The code inside the componentDidMount() {
this.paint(findDOMNode(this.REF.current), this.props.styles);
} I would love to know if what I am attempting can be achieved with only refs. Thanks. |
There is an application scenario, there is a component Overlay, which needs to be attached to a third-party component and obtain the position of the DOM element of the third component, such as: |
This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment! |
Big fat bump, stale bot sucks |
This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment! |
We are using an internal-developed WYSIWYG as a developer framework and cms. It's entire logic is written around findDomNode. Basically all the reusable (random-sourced) components, are wrapped around with an abstract 'wrapper', that attaches all kids of handlers from mouseEnter to drop to the domNode created by that component. As a result, when hovering any component in the tree, you can bubble up to closest library ui-component, then you build a node tree and can modify it separately under any resolution, ab-testing condition or any other context. This case has been stated quite a few times and it's entirely impossible to do via refs. IMO wysiwyg cms as the development tool is actually the next step in applying redux to build web-applications, as the CMS itself may take away all the extra logic, adding editor-layers, such as: Adaptivity, Language, ContentMapping, Extra Styling, AB-Testing, WebAnalytics, WebOptimization etc, and even move e2e testing to a whole new level, by allowing to record testing cases with attachment to concrete nodes. So we're sincerely hoping findDOMNode will always be a viable option. Moving it into a separate package but still supporting in react, just like prop-types seems like the best options here. |
Fragment event handlers (#12051) would be good to have. It would allow replacing |
React 19 is about to be released. I was wondering if there's any update on this issue? |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
@childrentime I've hidden your comment - publishing packages that use React internals like this will block unsuspecting users from upgrading, please do not do this. |
OK. Really sorry about that. |
import React, { useRef, useEffect } from 'react';
interface IProps {
children?: React.ReactNode;
as?: keyof JSX.IntrinsicElements | React.ComponentType<any>;
}
const ExposureWrapper: React.FC<IProps> = ({ children, as: Component = 'div', ...rest }) => {
const ioRef = useRef<IntersectionObserver | undefined>();
const domRef = useRef<any>(null);
useEffect(() => {
const domElement = domRef.current;
console.log('domElement', domElement)
if (!ioRef.current) {
ioRef.current = new IntersectionObserver(handleVisibilityChange);
}
const io = ioRef.current;
if (domElement && io) {
io.observe(domElement);
}
return () => {
if (domElement && io) {
io.unobserve(domElement);
io.disconnect();
}
};
}, []);
if(typeof Component === 'string') {
return React.createElement(Component, { ...rest, ref: domRef }, children);
}else if(typeof Component === 'function') {
// need to assign ref to component's first child
const Result = React.createElement(Component,{ ...rest },children);
return Result;
}
};
export default ExposureWrapper;
function handleVisibilityChange(entries: IntersectionObserverEntry[]) {
console.log('handleVisibilityChange', entries)
} For me, findDOMNode can easily accomplish this component, but if not used, it seems like there's no other way. |
Timeline
findDOMNode
is discouraged but accepted for certain use casesforwardRef
is introduced:It can be used in HOCs to avoid using
findDOMNode
on the enhanced componentfindDOMNode
is deprecated inReact.StrictMode
React.Concurrent
mode is released:This mode extends
React.StrictMode
in a way thatfindDOMNode
is deprecated in that mode too.React.Concurrent
modefindDOMNode use cases
If you have more use cases please let me know. I only started with some examples from
mui-org/material-ui
.with a planned alternative
State of
forwardRef
react
has 3.4M downloads/week.hoist-non-react-statics
(3.9M downloads/week; not clear what percentage is 2.x)A utility mainly used in HOCs and encouraged to use in the official react docs. However everyone stuck at
2.x
will likely encounter issues withforwardRef
since that versiondoes not handle any
react@^16.3
features. ^3.2.0 should have no issues apart from some minorissues with propTypes hoisting from
forwardRef
toforwardRef
. The latest stable from zeit/next still uses that outdated version. However the latest canary for 7.0.3 does not.react-docgen (400k downloads/week)
Not recognized as a valid component definition. PR open at reactjs/react-docgen#311.
react-redux (1.4M downloads/week)
connect
does properly forward their refs in the beta release of 6.x. No timeline for stable release givenhowever 3 betas have already been released so it's probably soon.
react-router (1.4M downloads/week)
withRouter
is planned to forward refs (ReactTraining/react-router#6056#issuecomment-435524678).However no comment about the other components and no major release candidate is published.
display name
React.forwardRef
components are recognized byreact-devtools
. However when wrappedin a HOC it's very likely that the display name is lost. See facebook/react#14319
The issue
Assumptions:
React.ConcurrentMode
make that component not usable in development mode because of all the noise it adds in those cases.
Noise because it's not actionable if that component is from a 3rd party library.
If none of those applies to you then you probably don't have an issue with
findDOMNode
deprecation.The mode of a partial tree can only be made more restrictive but not loosened up. If
you wrap your tree in
React.StrictMode
and use a component from a 3rd party librarythat 3rd party library has to be
React.StrictMode
compliant too.This means that you can't use
React.StrictMode
effectiveley. This might be ok since it's for development only anyway and has no implications for production. However Concurrent mode can have actual implications for production. Since it is new and the community wants to use new things libraries have to make sure that they are strict mode compliant too.In addition between the relase of an alternative in the form of
React.forwardRef
and the deprecation only 7 months have passed. One could argue that this is plenty of time but (at least from my perspective) the work on migrating fromfindDOMNode
to refs andforwardRef
was postponed becausefindDOMNode
was not deprecated yet. However the actual deprecation happened one day before the release ofunstable_ConcurrentMode
virtually giving no time to migrate.We'll have to see when a stableReact 16.x Roadmap was release pointing towards Q2 2019 as a release date of stable16.7
release will happen but assuming this happens today only a month has passed between deprecation and virtual removal.React.Concurrent
mode. This relaxes pressure for library maintainers quite a bit IMO.Conclusion
Refs are not a viable upgrade path to replace
findDOMNode
yet.Until refs are usable without headaches from forwarding refs
findDOMNode
should be undeprecated.Releated
The text was updated successfully, but these errors were encountered: