简体   繁体   中英

ReactJS event-driven updates on parent component

I have some components that are buried deep inside their parents. I have a user variable, which is fetched at the absolute top of this chain (a ProfilePage component), and passed all the way down with props. But, sometimes the user updates its profile in a small component on the page (the AboutMe component for example). My current flow is to have the AboutMe component update the user via ajax, and then trigger an event called 'profileupdate' on my pubsub. When 'profileupdate' is triggered, the ProfilePage fetches the user again via ajax, and so all the props are updated. This tactic appears to work, but it almost always gives:

Uncaught Error: Invariant Violation: replaceState(...): Can only update a mounted or mounting component.

I know this error is coming from the ProfilePage not being mounted and replacing its user state.

Here is an example of a place where I do this:

In the ProfilePage component:

var ProfilePage = React.createClass({
  getInitialState: function() {
    return {};
  },

  updateUser: function() {
    this.setUser('/api/users/' + this.props.params.userId);
  },

  setCurrentUser: function(currentUser) {
    this.setState({ currentUser: currentUser });
  },

  resetCurrentUser: function() {
    auth.getCurrentUser.call(this, this.setCurrentUser);
  },

  componentWillReceiveProps: function(newProps) {
    this.setUser('/api/users/' + newProps.params.userId);
  },

  componentDidMount: function() {
    PubSub.subscribe('profileupdate', this.updateUser);
    PubSub.subscribe('profileupdate', this.resetCurrentUser);
    this.resetCurrentUser();
    this.updateUser();
  },

  setUser: function(url) {
    $.ajax({
      url: url,
      success: function(user) {
        if (this.isMounted()) {
          this.setState({ user: user });
        } else {
          console.log("ProfilePage not mounted.");
        }  
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.userId, status, err.toString());
      }.bind(this)
    });
  },

Then, in the AboutInput component: ProfilePage --> ProfileBox --> About --> AboutInput

updateAbout: function(text) {
  var url = '/api/users/' + this.props.user._id;
  var user = this.props.user;
  user.about = text;
  $.ajax({
    url: url,
    type: 'PUT',
    data: user,
    success: function(user) {
      auth.storeCurrentUser(user, function(user) {
        return user;
      });
      PubSub.publish('profileupdate');
      this.propagateReset();
    }.bind(this),
    error: function(xhr, status, err) {
      console.error(status, err.toString());
      this.propagateReset();
    }.bind(this)
  });
},

The takeaways here are that the ProfilePage fetches the user again when the profileupdate event is triggered.

Am I using the wrong tactics here? How can I handle this kind of update? And, can I force the ProfilePage to become mounted? That would be really ideal. Also, interestingly, whether or not the console logs that the ProfilePage is unmounted, the user still updates.

I really would take a look at Facebook's FLUX implementation. It will simplify your workflow tremendously. I am guessing that is not an option at this moment, but make sure your components are unsubscribing when removing them. For eg:

componentWillUnmount: function () {
    var self = this;
    self.store.removeListener('change', self._setState);
    self._setState = null;
}

Or in your case unsubscribe using the method above from your PubSub implementation when a component is being replaced. So 'profileupdate' is being called on a React component that has been unmounted. Something like.

componentWillUnmount: function () {
    PubSub.unsubscribe('profileupdate', this.updateUser);
    PubSub.unsubscribe('profileupdate', this.resetCurrentUser);
    this.updateUser = null;
    this.resetCurrentUser = null;
}

It is exactly what the error states. You trying to set state on a React component that is not longer mounted, or has been removed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM