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

Unsafe Lifecycle warning in React >= 16.3 #6060

Closed
morlay opened this issue Apr 3, 2018 · 15 comments
Closed

Unsafe Lifecycle warning in React >= 16.3 #6060

morlay opened this issue Apr 3, 2018 · 15 comments

Comments

@morlay
Copy link

morlay commented Apr 3, 2018

Version

react-router@create-context-api
[email protected] under React.StrictMode

Steps to reproduce

use react-router under React.StrictMode

Expected Behavior

without warning

Actual Behavior

warning for componentWillReceiveProps componentWillMount

@morlay
Copy link
Author

morlay commented Apr 3, 2018

For Router, we could write like this to fix the componentWillMount warning.
It works, but not sure is this a correct way.

https://github.com/ReactTraining/react-router/blob/react-context-api/packages/react-router/modules/Router.js#L79-L81

render() {
    const { children } = this.props;
    return (
      <RouterContext.Provider value={this.getChildContext()}>
        <HistoryListener history={this.props.history} onLocationChange={() => { this.setState(...) }} />
        {children ? React.Children.only(children) : null}
      </RouterContext.Provider>
    );
 }
class HistoryListener extends React.Component<{ history: History, onLocationChange: () => void }> {
  unlisten: any = null

  componentDidMount() {
    this.unlisten = this.props.history.listen(() => {
      this.props.onLocationChange()
    })
  }

  componentWillUnmount() {
    if (this.unlisten) {
      this.unlisten()
    }
  }

  render() {
    return null
  }
}

@pke
Copy link

pke commented Apr 3, 2018

This should be fixed in v3 too.

@timdorr
Copy link
Member

timdorr commented Apr 3, 2018

@morlay No need to farm that out to another component. Instead, we can move to cDM and simply do a second check for if the location changed during that time.

Another option, if we wanted to go all-in on 16+, would be to use a setState callback function for the initial read from history.

@timdorr
Copy link
Member

timdorr commented Apr 3, 2018

Easy next step: Upgrade to React 16.3 locally and add a failing test, like I did here: reduxjs/react-redux#918

@morlay
Copy link
Author

morlay commented Apr 3, 2018

@timdorr
In StrictMode,constructor static getDerivedStateFromProps and render will be double-invoked.

constructor -> static getDerivedStateFromProps -> render -> componentDidMount
There are no opportunity to do the second check. So i think to move history listener to another component.

Then we can have
Router#constructor -> Router.getDerivedStateFromProps -> Router#render -> HistoryListener#constructor -> HistoryListener#componentDidMount -> Others Components >Router#componentDidMount

logic in HistoryListener#componentDidMount will be same as old Router#componentWillMount.

I tried providing another callback method in context to omit location changes or proxy history push replace methods.
It only handle changes from children of Router and will miss others changes from history itself, like navigating by redux actions.

@timdorr
Copy link
Member

timdorr commented Apr 4, 2018

That's fine. We can add checks to avoid double execution.

@grzegorzjudas
Copy link

Is there a PR to fix this?

@gustavo-depaula
Copy link

I opened a PR, but I need someone to help me test this.

@frehner
Copy link
Contributor

frehner commented Sep 19, 2018

Created #6341 which hopefully is on the path of resolving this issue.

@mjackson
Copy link
Member

This is fixed in master. You can see some of my commits here and here.

You can expect a new beta release sometime this week! 😅

@jaredpalmer
Copy link
Contributor

WOOOO!

@morlay
Copy link
Author

morlay commented Sep 26, 2018

@mjackson
There will be double listened under StrictMode, and the first listener will be an un-controlled callback and may cause some issue when history changed.
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Router.js#L42-L44

as I said #6060 (comment)

if we need to add listener in constructor, may try to unlisten before adding listener

this.unlisten && this.unlisten()
this.unlisten = ....
```;

@timdorr
Copy link
Member

timdorr commented Sep 26, 2018

Yeah, that listen call should be in a cDM. It's going to leak in server rendering too.

@mjackson
Copy link
Member

mjackson commented Oct 1, 2018

Ah, good call @morlay. I'll fix in the next beta release.

@timdorr We can't use cDM because of the way <Redirect> works. But we can filter out the listen call based on whether we're in a persistent environment (the browser) or not.

@mjackson
Copy link
Member

mjackson commented Oct 1, 2018

@morlay @timdorr Let's follow up in #6363

@lock lock bot locked as resolved and limited conversation to collaborators Nov 30, 2018
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.

8 participants