Skip to content
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

React.StrictMode throws warning because of findDOMNode use #2154

Closed
optunetobi opened this issue Oct 25, 2018 · 62 comments · Fixed by #2563
Closed

React.StrictMode throws warning because of findDOMNode use #2154

optunetobi opened this issue Oct 25, 2018 · 62 comments · Fixed by #2563

Comments

@optunetobi
Copy link

Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of StyledComponent which is inside StrictMode. Instead, add a ref directly to the element you want to reference.

I am aware that it is only run in development-mode, but it clutters console output when trying to find errors.

@Fer0x Could we change this here somehow? (by not using findDOMNode)

I don't think it is a problem, not even sure if there is a proper solution. Keeping it for later reference.

Originally posted by @optunetobi in #2073

@optunetobi
Copy link
Author

Maybe React.createRef() could be used (since React 16.3).
RFC: facebook/react#12162
Release notes: https://github.com/facebook/react/blob/master/CHANGELOG.md#1630-march-29-2018

@Fer0x
Copy link
Contributor

Fer0x commented Oct 25, 2018

@optunetobi
I have tried to replace findDOMNode with ref. I made it like it described in following issue facebook/react#13029, but it doesn't work for cases with functions like styled(() => <div />) which are main target for classNameUseCheckInjector warning.
So this issue needs further investigation.

@Fer0x
Copy link
Contributor

Fer0x commented Oct 25, 2018

I think we will drop findDOMNode only when ref for Fragments be implemented. See facebook/react#13841 (comment)

@ziimakc
Copy link

ziimakc commented Oct 30, 2018

Oh, so that was styled component's that is throwing this error. Took me a long time to find after react update. Any plans to fix this?

@quantizor
Copy link
Contributor

quantizor commented Oct 30, 2018 via email

@sag1v
Copy link

sag1v commented Nov 1, 2018

@probablyup for now findDOMNode will stay?
StrictMode is killing it 🤷‍♂️

@quantizor
Copy link
Contributor

Yeah I mean StrictMode is optional so just don't use it until a replacement API is available 🤷‍♂️

@sag1v
Copy link

sag1v commented Nov 1, 2018

Yeah, the problem is when my lib depends on sc and others depends on my lib while using StrictMode. But i guess there's nothing sc can do about it now

@ganapativs
Copy link

Same issue here while using React Suspense. Not using StrictMode explicitly, but using ReactDOM.createRoot(rootElement).render(<App />). Is StrictMode enabled by default while using ReactDOM.createRoot syntax?

Demo:
Edit 91xnz9j44r

@hagnerd
Copy link

hagnerd commented Nov 6, 2018

StrictMode is enabled by default when using ReactDOM.createRoot and/or React.ConcurrentMode.

@Macil
Copy link

Macil commented Nov 6, 2018

@Fer0x The Fragment ref solution will only give you the DOM elements if the React elements immediately inside the Fragment are raw HTML elements rather than React components. (Or it will work if they're React components that forward their ref to a raw HTML element.) This might be fine for the case here, just pointing out that it has restrictions and there are alternatives you can do now with similar restrictions.

If you're fine with the above restriction, and also fine with the restrictions that there's only one DOM element and that the user can't use string refs on the element, then you can implement this now by using React.cloneElement: (I'm using some Flow/TypeScript-like syntax for type annotations here)

class Foo extends React.Component {
  _childEl: ?HTMLElement;
  _childRef = (el: ?HTMLElement) => {
    this._childEl = el;
    if (this.props.children.ref) {
      setRef(this.props.children.ref, el);
    }
  };

  render() {
    return React.cloneElement(
      this.props.children,
      {ref: this._childRef}
    );
  }
}

where setRef is defined as this. (If you don't actually need a ref to the DOM element, and just need to read or set props on it, then you don't even need the dirty setRef-related parts and therefore there's no restriction on the user about string refs.) I'm planning on using a solution like this in react-draggable-list (to re-implement the dragHandle function seen here) to avoid findDOMNode.


Another more general solution is to change styled(() => <div />) so that the user has to pass a ref onto their DOM element: styled(domRef => <div ref={domRef} />). I used this solution when upgrading react-float-anchor to not use findDOMNode. The downside is that it's explicit, and if the end-user wants their own ref on the same element they need to pass the given ref onto, then things get awkward (the user has to implement setRef and use it like this). In my case, end-users can usually just add a separate wrapper div, pass the ref from react-float-anchor onto the wrapper div, and then put their ref on their element, so users can often avoid awkwardness, but I'm not especially happy with it. I'm interested in following this thread and others to see if there are better possible patterns.

@zaguiini
Copy link

Any news on this?

@quantizor
Copy link
Contributor

Hound the React team if you want movement on this... there's nothing we can do until a replacement API is available other than disabling the checker for strict mode (and is there even a way to detect that you're inside strict mode?)

@zaguiini
Copy link

Well, if there was a way to disable StrictMode, people would do it, wouldn't they? xD

Thanks anyway!

@quantizor
Copy link
Contributor

if there was a way to disable StrictMode

It's entirely optional to use StrictMode.

@zaguiini
Copy link

if there was a way to disable StrictMode

It's entirely optional to use StrictMode.

Unless you're using React in Concurrent mode.

@quantizor
Copy link
Contributor

Which is also optional :)

There is a cost to using new features before they've had time to mature.

@zaguiini
Copy link

Yeah :( But luckily the only trade-off in this case is this annoying warning.

@Jessidhia
Copy link
Member

Jessidhia commented Nov 15, 2018

That warning is also incorrect when styled is used as a HOC HOC 😆

function ClassNameProvider(props: {
  className?: string
  children(className: string): React.ReactNode
}) {
  return props.children(props.className!) as JSX.Element | null
}
const FooClass = styled(ClassNameProvider)`/* styles */`

; (
  <FooClass>{fooClass => (
    <>
      {/* use fooClass, possibly conditionally,
        * possibly as another prop that only uses it conditionally, etc
        */}
    </>
  }</FooClass>
)

(this was my solution to the activeClassName problem in react-router NavLink)

@Fer0x
Copy link
Contributor

Fer0x commented Nov 15, 2018

@Kovensky I believe that such cases are quite rare, so you can disable warning with suppressClassNameWarning prop.

@Fer0x
Copy link
Contributor

Fer0x commented Nov 15, 2018

@zaguiini Deprecating of findDOMNode in Strict mode is not only our pain. Third party libraries actively use it. For example: mui/material-ui#13394, twobin/react-lazyload#209, akiran/react-slick#1406, reactjs/react-transition-group#429, necolas/react-native-web#1151

@quantizor
Copy link
Contributor

quantizor commented Nov 15, 2018 via email

@sag1v
Copy link

sag1v commented Nov 15, 2018

The Fragment ref solution will only give you the DOM elements if the React elements immediately inside the Fragment are raw HTML elements rather than React components. (Or it will work if they're React components that forward their ref to a raw HTML element.)

@Macil How do you know? is there any RFC or draft implementation for this?

Honestly I wouldn’t be surprised if they decide to undeprecate it
eventually

@probablyup I'm not sure, it seems they don't think there is a solid case for keeping it. see this tweet

@Macil
Copy link

Macil commented Nov 15, 2018

@sag1v Actually, I'm much less sure about that now. (One of the reasons I saw about removing findDOMNode was because it breaks encapsulation, which the Fragment ref feature wouldn't be much of an improvement if it operated on DOM nodes. Also, the Fragment ref feature would be really handy in a number of situations I've run into if it operated on components instead of DOM nodes. But it was suggested in a thread specifically about alternatives to findDOMNode and it may address the "it becomes difficult to know if any given DOM node might need to be found [later]" technical issue, so I'm not sure what to think on re-reading the thread.) I've asked for clarification in that thread.

@zaguiini
Copy link

@zaguiini Deprecating of findDOMNode in Strict mode is not only our pain. Third party libraries actively use it. For example: mui-org/material-ui#13394, jasonslyvia/react-lazyload#209, akiran/react-slick#1406, reactjs/react-transition-group#429, necolas/react-native-web#1151

I see that. Luckily I'm very skeptical about adding new libraries to a project so I keep my package.json short and simple

@gaearon
Copy link
Member

gaearon commented Dec 4, 2018

It looks like you're using findDOMNode only for a DEV-time warning. Is that warning really so valuable?

@gaearon
Copy link
Member

gaearon commented Dec 4, 2018

Hound the React team if you want movement on this... there's nothing we can do until a replacement API is available other than disabling the checker for strict mode (and is there even a way to detect that you're inside strict mode?)

Has anyone filed an issue on the React repo detailing a specific use case?

I don't know much about how styled-components uses this but solution with cloneElement in #2154 (comment) sounds reasonable to me if you want to preserve this behavior. Could you please describe why it doesn't work for you? Thanks.

@gaearon
Copy link
Member

gaearon commented Dec 4, 2018

Unless you're using React in Concurrent mode.

You shouldn't be though. It's not stable. Less stable than Hooks, even.

@Fer0x
Copy link
Contributor

Fer0x commented Dec 5, 2018

@exogen
I was thinking about this approach. However, it’s not enough for cases when portals with locally created iframes are used.

@42shadow42
Copy link

Strict mode also appears to produce the following warnings:

Warning: Unsafe lifecycle methods were found within a strict-mode tree:
in StrictMode
componentWillMount: Please update the following components to use componentDidMount instead:

Warning: Unsafe lifecycle methods were found within a strict-mode tree:
in StrictMode
componentWillReceiveProps: Please update the following components to use static
getDerivedStateFromProps instead:

react-dom.development.js:506 Warning: Legacy context API has been detected within a strict-mode tree:
in StrictMode

Please update the following components:

@quantizor
Copy link
Contributor

quantizor commented Dec 6, 2018 via email

@sag1v
Copy link

sag1v commented Dec 6, 2018

@42shadow42 I think it would be better to open a separate issue for "Unsafe lifecycle methods" as this issue is mostly focused on findDOMNode.

@quantizor
Copy link
Contributor

quantizor commented Dec 6, 2018 via email

@sag1v
Copy link

sag1v commented Dec 6, 2018

@probablyup I think the same, but a new issue (with the detailed initial template) may discover that, or maybe a closed issue about this topic will pop up on people's search if they have the same issue instead of it being on the bottom of this long and unrelated issue. just my 2 cents

@gaearon
Copy link
Member

gaearon commented Dec 6, 2018

Big thanks to @Fer0x for actually explaining what styled-components is doing with findDOMNode and why — which is missing from this thread and made it difficult to respond to it initially. For future cases where you feel React is getting in your way I’d appreciate if you could provide similar explainers. This was very helpful.

That's issue can be reproduced when custom React component is passed to styled() factory. In this case styled-components create className and pass it as prop to that custom React component. If component is not supposed of supporting className prop, warn of incorrect usage should be thrown.
So, in styled-components code, we need to find DOM node with generated className.

This approach seems a bit flawed to me because the general expectation is that it should be valid for a child component to not render the node with a class name (e.g. due to some early exit condition) and maybe render it later. So it’s a heavy handed assumption that I must render that className regardless.

Of course I did not design SC API and don’t have context on your decisions — I’m just looking from the point of view of a React component user. I generally expect that I can add return null into any component at any point in time and it shouldn’t cause me to see warnings. It’s a valid React feature.

function Story({ className, story }) {
  if (story.isLoading) {
    return null; // This shouldn't cause a warning but seems like it would?
  }
  return <div className={className} />;
}

Fragments present a different issue (findDOMNode will only find the first node) that can also cause misleading warnings. So I think we can agree the warning seems to stand in the way of using normal React features somewhat.

Now, maybe the mistake is very common and so the benefits of having a warning outweigh the false positives and other issues. I would argue that the issue might be inherent to this API that “pretends“ to override component’s styles with styled(Button) and might give people the wrong mental model. But maybe that’s also part of the appeal of styled components — it feels more familiar than e.g. CSS-in-JS, and styled(Button) kinda feels like selector overrides.

One thing you could do is to make className an object with custom toString that returns a class name in DEV. React would (AFAIK) toString it before rendering. That would let you detect that toString was called. If it's not called within some timeout, you could warn in DEV. In production you could still use strings.

Yet another thing you could is to add a unique identifier to the className in DEV. (So, effectively, it would be two class names — one for the component and one for the instance — separated by spaces.) Then you could do the document.querySelector check suggested in #2154 (comment) without worrying about conflicts. Seems like that would be the easiest fix unless I’m missing something.

Hope that helps. As always, we’re happy to support you and try to find solutions to problems you encounter. But since we don’t use styled-components it’s difficult to understand the challenges you’re running into until we get a nice explanation like facebook/react#14357 (comment). Instead of arguing whether the API needs to be brought back or not I think it would be much more productive if we always frame a discussion about a specific use case you need to solve.

@quantizor
Copy link
Contributor

This approach seems a bit flawed to me in general because it seems to me that should be valid for a child component to not render the node with a class name (e.g. due to some early exit condition) and render it later. So it’s a bit of a heavy handed assumption that I must render that className regardless.

That's a valid criticism. It's meant to be a dev-time warning that the consumer can opt out of via prop flag.

@OzairP
Copy link

OzairP commented Dec 12, 2018

@liuyangc3 sounds like a use case for refs or portals. Regardless you should make your use case known on the React issue hereunless you're talking about StyledComponenets which it doesn't look like you are.

@probablyup would you mind addressing Dan's proposals on replacing the implementation of class name forwarding? Which one would be the best in terms of SC?

@manspaniel
Copy link

Getting this when trying to use styled components within WordPress v5's new editor, which uses React, since their editor is wrapped in StrictMode 🤦‍♂️

Until either WP remove StrictMode, or styled-components is updated to use refs, I've been using this little hack to prevent the error from taking up 50 lines of my console each reload. Obviously not a nice one, but it works. Just thought I'd share 👍

;(() => {
  const oldLogError = console.error
  console.error = function(...args) {
    if (typeof args[0] !== 'string' || args[0].indexOf('is deprecated in StrictMode') === -1) {
      oldLogError.apply(console, args)
    }
  }
})()

@OzairP
Copy link

OzairP commented Jan 30, 2019

@manspaniel if you don't want deprecation warnings then you shouldn't use StrictMode. That defeats the whole purpose.

@manspaniel
Copy link

@OzairP I understand. As I mentioned, WordPress' Gutenberg editor is wrapped in a React.StrictMode, and I'm interfacing with it by passing it components to render. I have no control over the top-half of the app and so cannot "not use StrictMode".

Like I said, it's a hack so that I can continue working without interruption and I thought it might be useful for others in a similar situation.

@OzairP
Copy link

OzairP commented Jan 30, 2019

@manspaniel yikes! Thought you were developing it!

@quantizor
Copy link
Contributor

I'm open to a PR just removing this dev check

@rwieruch
Copy link

rwieruch commented May 28, 2019

I ran into this warning with styled-components 4.2 with StrictMode.

Is this possible or is it something on my side?

@f22hd
Copy link

f22hd commented Apr 19, 2020

Disabling strict mode will remove the warning messages,
You can find the tag in index.js file .

@gaearon
Copy link
Member

gaearon commented Apr 19, 2020

Update your styled-components.

@preshonyee
Copy link

preshonyee commented Apr 23, 2020

Update your styled-components.

I'm actually using styled-components and I have the latest version of styled-components installed.
Although, I'm also using antd design library and I particularly get this error when I render a button component. I'm still looking into this.

Here's the error I'm getting below:

Console
index.js:1 Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an 

instance of Wave which is inside StrictMode. 

Instead, add a ref directly to the element you want to reference. 

Learn more about using refs safely here: https://fb.me/react-strict-mode-find-node

in button (created by Context.Consumer)

in Wave (created by Context.Consumer)

in Button (at primaryButton.tsx:46)

in div (created by styled.div)

in styled.div (at primaryButton.tsx:45)

in PrimaryButton (at login.tsx:57)

in div (created by FormItemInput)

in div (created by FormItemInput)

in div (created by Context.Consumer)

in Col (created by FormItemInput)

in FormItemInput (created by FormItem)

in div (created by Context.Consumer)

in Row (created by FormItem)

in FormItem (at formItem.tsx:12)

in FormItem (at login.tsx:56)

in form (created by ForwardRef(Form))

in ForwardRef(Form) (created by ForwardRef(InternalForm))

in SizeContextProvider (created by ForwardRef(InternalForm))

in ForwardRef(InternalForm) (at forms.tsx:17)

in Forms (at login.tsx:30)

in div (created by styled.div)

in styled.div (at layouts.tsx:9)

in Layouts (at login.tsx:23)

in Login (created by Context.Consumer)

in Route (at App.tsx:17)

in Switch (at App.tsx:16)

in Router (created by BrowserRouter)

in BrowserRouter (at App.tsx:13)

in App (at src/index.tsx:9)

in StrictMode (at src/index.tsx:8)

`

@rogemus
Copy link

rogemus commented May 15, 2020

@gaearon Sadly updating styled-components to 5.1.0 didn't resolve this issue.

Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of RefFindNode which is inside StrictMode. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: https://fb.me/react-strict-mode-find-node
    in button (created by Button)
    in RefFindNode (created by Ref)
    in Ref (created by Button)
    in Button (created by LoginForm)
    in form (created by Form)
    in Form (created by LoginForm)
    in LoginForm (created by Login)
    in div (created by styled.div)
    in styled.div (created by Login)
    in div (created by styled.div)
    in styled.div (created by Login)
    in div (created by styled.div)
    in styled.div (created by Login)
    in Login (created by LoginView)
    in LoginView (created by Context.Consumer)
    in Route (created by App)
    in Switch (created by App)
    in Router (created by HashRouter)
    in HashRouter (created by App)
    in App
    in ApolloProvider
    in StrictMode

@rogemus
Copy link

rogemus commented May 15, 2020

@probablyup Can we reopen this issue or do you want me to create new one?

@kitten
Copy link
Member

kitten commented May 15, 2020

@rogemus Are you sure this warning is coming from styled-components? From that stack it could also be RefFindNode going off its name 😅

@quantizor
Copy link
Contributor

Yeah it's definitely not s-c, we don't use findDOMNode anymore

@kfekairi
Copy link

kfekairi commented May 22, 2020

Quote

Update your styled-components.

I'm actually using styled-components and I have the latest version of styled-components installed.
Although, I'm also using antd design library and I particularly get this error when I render a button component. I'm still looking into this.

Here's the error I'm getting below:

`

index.js:1 Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an 

instance of Wave which is inside StrictMode. 

Instead, add a ref directly to the element you want to reference. 

Learn more about using refs safely here: https://fb.me/react-strict-mode-find-node

in button (created by Context.Consumer)

in Wave (created by Context.Consumer)

in Button (at primaryButton.tsx:46)

in div (created by styled.div)

in styled.div (at primaryButton.tsx:45)

in PrimaryButton (at login.tsx:57)

in div (created by FormItemInput)

in div (created by FormItemInput)

in div (created by Context.Consumer)

in Col (created by FormItemInput)

in FormItemInput (created by FormItem)

in div (created by Context.Consumer)

in Row (created by FormItem)

in FormItem (at formItem.tsx:12)

in FormItem (at login.tsx:56)

in form (created by ForwardRef(Form))

in ForwardRef(Form) (created by ForwardRef(InternalForm))

in SizeContextProvider (created by ForwardRef(InternalForm))

in ForwardRef(InternalForm) (at forms.tsx:17)

in Forms (at login.tsx:30)

in div (created by styled.div)

in styled.div (at layouts.tsx:9)

in Layouts (at login.tsx:23)

in Login (created by Context.Consumer)

in Route (at App.tsx:17)

in Switch (at App.tsx:16)

in Router (created by BrowserRouter)

in BrowserRouter (at App.tsx:13)

in App (at src/index.tsx:9)

in StrictMode (at src/index.tsx:8)

`

i have the same problem, is there any solution ?

@quantizor
Copy link
Contributor

quantizor commented May 22, 2020 via email

@kitten
Copy link
Member

kitten commented May 24, 2020

If you're seeing this, you can check findDOMNode` across our codebase, and you'll see, it does not occur there anymore. Hence, please check your other dependencies or your own code instead. It's fine to ask for help (preferably on Spectrum or another help platform though) but posting a stack/console output won't lead to any new revelations or any ability for anyone else to help you unfortunately 😅

@styled-components styled-components locked as resolved and limited conversation to collaborators May 24, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.