React: componentDidReceiveProps Please

Created on 27 Feb 2015  ·  94Comments  ·  Source: facebook/react

I would like to humbly request a componentDidReceiveProps hook, often times I would like to do something on both componentWillMount and componentWillReceiveProps, but because this.props hasn't been set yet I am forced to pass props around instead of reading directly from this.props.

Before New Hook

componentWillMount() {
  this.setup(this.props.id);
}

componentWillReceiveProps(next) {
  this.setup(next.id);
}

setup(id) {
  UserActions.load(id);
}

After New Hook

componentWillMount() {
  this.setup();
}

componentDidReceiveProps() {
  this.setup();
}

setup() {
  UserActions.load(this.props.id);
}

In this simple example it may seem like a small thing, but often times the passing of props runs deep and instead of conveniently referencing this.props one is forced to plumb the props throughout the component.

Please consider adding componentDidReceiveProps as a hook to leverage the same code that is leveraged in componentWillMount without forcing both to plumb props throughout the component.

Most helpful comment

@syranide The trouble is when setup needs to call methods that also need props, which needs to call methods which also needs props... Eventually your entire component is plumbing around props.

All 94 comments

Why not setup(props)?

Looking through my own projects I can see I've done this where I had similar needs (another one is deriving a chunk of state based on props), just passing in a different set of props when needed so you only have to pass something extra when reusing, without duplicating knowledge of which props are needed:

setup(props) {
  props = props || this.props
  UserActions.load(props.id)
}

@syranide The trouble is when setup needs to call methods that also need props, which needs to call methods which also needs props... Eventually your entire component is plumbing around props.

+1 seems like a bunch of needless wiring up in the app with the current pattern. Could be a concise standardized way to solve the problem. Seen a bunch of people get burned on this.props inside of ComponentWillReceiveProps, a clear sign that it's not intuitive.

+1, I also find this frustrating.

No longer find this to be an issue.

+1, I am also finding myself passing around props a lot. Similar to @insin above I was using default params for awhile:

setup(props = this.props) {
  doSomething(props);
}

But decided it was an anti-pattern due to the subtle bugs it can cause if you forget to pass newProps.

+1

I think the reason it's not available is because this.props and this.state _always_ correspond to rendered values. There's no way to implement componentDidReceiveProps that doesn't trigger another render without breaking this constraint.

Most of the times, if you're using componentWillReceiveProps, you actually want either a higher order component a la Relay, or something like observe hook proposed for React 0.14.

+1
Also, you can implement componentDidReceiveProps without changing this.props or this.state. If all you need to do is read from these, then you won't trigger another render. If you're writing to props or state within this proposed function call, then you're shooting yourself in the foot, but that's the same case for every other method in the lifecycle.

+1

I want to be able to respond to the new props event when shouldComponentUpdate returns false, so in my case I can't use componentDidUpdate.

+1

+1

What about componentWillMount and componentWillUpdate @iammerrick

I want to be able to respond to the new props event when shouldComponentUpdate returns false, so in my case I can't use componentDidUpdate.

Use componentWillReceiveProps?

+1

+1 this helps with DRY code and simplifying logic.

I would be open for componentDidReceiveProps to be called on the initial setup to make the example starting this thread just a single function:

componentDidReceiveProps() {
  UserActions.load(this.props.id);
}

Thoughts?

componentWillReceiveProps() {
  setTimeout(()=> {
    if(this.isMounted()) {
      this.componentDidReceiveProps();
    }
  });
}
componentDidReceiveProps() {
  UserActions.load(this.props.id);
}

What do you think?

@YourDeveloperFriend I think it depends on how it works relative to other lifecycle hooks and rendering delays due to cascading timeouts on nested components.

These sorts of lifecycle hooks should be called synchronously in a pass that is guaranteed to be called before render. I haven't studied React's codebase, but I'm assuming render isn't called on a timeout.

Most likely, the best solution is going to be something along the lines of registering pre-rendering hooks or to mark components that have had their props changed so the render logic calls componentDidReceiveProps before calling render.

+1 please. Having the same issue where I have to pass props around to just about every method in my component class. So ugly.

Nah, I'm good. There are better solutions.

+1

componentDidReceiveProps() already exists: it's called render(). However there can be a case (like in @iammerrick 's example) where you need to load/trigger/etc something before you perform a render. This means one and the only one thing: you do something wrong.

When you do things like

setup(id) {
    UserActions.load(id);
}

you introduce statefullness either in the component or (much worse) outside. Why do you ever need to load() data every time a component receives props (they aren't even guaranteed to be new)? If you do lazy-loading, the proper way is to request the data in the render() method:

render() {
    var actions = UserActions.load(id);
    if (actions) // render
    else // show spinner or return null, etc.
}

UserActions.load = function(id) {
    if (data) return data;
    else // request the data, emit change on success
}

@robyoder , passing arguments to functions isn't ugly. It may seem too verbose but it's natural in the programming language you've chosen. Trashing the API by adding a spare lifecycle method just to reduce verbosity is definitely ugly indeed.

So tell me, @me-andre, why do we have both componentWillUpdate and componentWillReceiveProps?

In my opinion, it's because they serve different purposes and both are helpful. Just like those two are not the same, render is not the same as the hypothetical componentDidReceiveProps because it's called for reasons other than new props; plus, you aren't given access to the previous props.

The benefit of the previous props is that you wouldn't have to load data every time; it can be conditional based on the props and whether they've changed.

Obviously, "[ugliness] is in the eye of the beholder", because adding a proper lifecycle method seems like a much cleaner solution to me.

Maybe there is another way to look at this...

Let's say the main goals here is are:

(1) reduce human error - I forgot to pass parameters or use props = props || this.props yet again
(2) to reduce non-value adding boilerplate code - why write something unnecessarily
(3) to reduce the cognitive burden of trying to decide which lifecycle methods to use - why do I need to think about this sort of thing, why do I keep using slightly different approaches depending on how I feel that day, etc

So maybe the solution space is around simplifying of both the use of React and the React API itself to achieve these goals.

If you think about the problem with these goals in mind, maybe some lifecycle methods could be merged and if you are interested in knowing it is initialization vs update, you can tell by the calling signature / arguments. For example: beforeRender(prevProps, prevState) or update(prevProps, prevState).

Personally, it feels like there are too many lifecycle methods and if they were more called consistently (without or with prevProps / prevState on initial pass and update), it could simplify my code and increase my productivity. Plus, the lifecycle method names are pretty long (I tend t copy / paste them around rather than typing them) and it is hard to remember which will / did's exist which makes me think they could / should be simplified.

@robyoder , the short answer why we have both componentWillUpdate and componentDidReceiveProps is there is a huge difference between them. They're similar of course, but mainly in being prefixed with componentWill.

| | state has changed | component did not update |
| --- | --- | --- |
| componentWillUpdate() | yes | no |
| componentWillReceiveProps() | no | yes |

As you may have noticed there are lifecycle points / conditions where one method gets called and the other does not. That's why we have 2 distinct lifecycle methods.

However componentDidReceiveProps() as it was described in this topic does not represent any component state or condition and it doesn't give an access to anything componentWillReceiveProps() does not. It just adds a slight syntactical sugar which may or may not seem useful or easier - this is a personal preference, not a technical requirement. And that is exactly the reason why it shouldn't be added: it's subjective. You say passing arguments is ugly - but you also said "[ugliness] is in the eye of the beholder". For me it seems like you've answered yourself.

@me-andre you might be responding to me (@kmalakoff) and @robyoder at the same time. Or maybe not, but I'll take this opportunity to move the discussion forward...

What I was raising was that perhaps thinking above the current API with these three above goals could give new insights or perspectives into how one might simplify the API to achieve them. Of course, after going through the exercise, we might end up at the same place.

Let's go through the exercise....

Let's assume this topic is being followed and +1-ed because there is something important to it and let's say the addition of componentDidReceiveProps is not a good idea because it increases the API surface area.

Keeping in mind the table and the 3 goals I put forward, does anyone have any ideas for simplify the API and / or another way to give the people in this thread what they desire and to not expand the API (maybe even reducing / simplifying it)?

@kmalakoff , the 3 points you talk about are connected with the API only in that you use the API when you encounter them. They aren't caused by a bad design.

The first problem you talk about is that lifecycle methods take different arguments. Well, that's perfectly natural - they serve different purposes. componentWillReceiveProps takes props not because the moon was full when this method was added - but because it's about receiving props which have not been yet assigned to the component. props are being passed only in methods where they (may) differ from this.props. This is actually a hint, not an issue.
The problem # 3 is that it's hard for you to decide which callback / approach to use. Well, that's not really a problem until the methods I talk above have the same signature. Once you have both state and this.state, props and this.props which are equal (read meaningless) in most methods, then you get entangled in spare options and alternatives.
The problem # 2 is about boilerplate code... well, React is a thin library - and it's beautiful, easy and strict because of that. If you want to make a wrapper which would simplify our lives and reduce the amount of code we write every day - why not do it? Publish it as your own npm that depends on react and you're done.

Regarding "there are too many lifecycle methods" - not yet and won't ever be if you stop requesting for new callbacks for which you don't have a technical need. Technical need is when you implement a complex component and in the middle of work you understand there is absolutely no way of doing it without some ugly hack. It's about "to trigger a tricky DOM method I need something to get called at the time the component does X but there's no such method and I cannot add one, so I use setTimeout and hopefully it wouldn't land earlier than I need". Not when you say "oh, I got tired of writing props = this.props".

And one more thing...
In a well-designed React application, you don't need most of the methods we talk about or you use them very rarely. getInitialState, componentWillMount, componentWillUnmount suffice 99% of the time. In my current project which is a relatively large commercial app, componentWillReceiveProps is used twice throughout all the app. I would not use it at all, but the environment (read browser) is imperfect - manipulating certain "stateful-in-itself" things such as scrollTop or relying on layout computations for future renders requires manual synchronizing between props transitions and DOM changes. However in most "normal" cases, you just don't need to know when props transition happens.

@me-andre based on my experience working with React, I believe there is room to improve / simplify the API. I'm not alone which is why this issue was raised in the first place and is being +1-ed.

If I answer by providing feedback on your analysis, I don't think it will really move this forward (which is why I have avoided doing it so far) since it is a bit biased towards the status quo and justifying the current API, but I'd be happy to provide feedback on potential solutions! People engaged in this issue are looking to explore improvement ideas and potential API changes to address the issues raised through their experience using React (like I outlined above).

Maybe you are right that the community should do the innovation to move the API forward and maybe have it rolled back into core or maybe the current API is perfect as is, but I think this is a great opportunity to consider what change may look like with the React team.

Maybe the reason you are making arguments as you are is because we are just users of the library with less depth of understanding on the currently API and decisions that went into it. Maybe you could pull in some other React team members who is equally experienced and knowledgeable as yourself to see if we get a different perspective and maybe even come up with a great solution or to get consensus that this is as good as it gets?

@kmalakoff , imagine React is an engine and 4 wheels. Now every time you want to go somewhere, you build the rest of the car and that eventually starts being annoying. Otherwise you complain about the need to remember low-level engine details and turning front wheels by hands doesn't feel the right way.
What I would recommend is either to get a complete vehicle (a full-fledged framework) or to build the needed components in the way that you can reuse them over time.
What I see in this thread is engine users complaining that the engine has no hydraulic system and no interior lights. What I feel is that we'd get a complete crap if we add these features to where they don't belong.
React is not a framework: it's more a diff-powered rendering engine that exposes a powerful component system. It has low level and minimalistic API yet powerful enough to enable you to build literally anything on top of it.
Please don't hesitate contacting me via email if you feel like I need to clarify something - I don't want this thread to become a tutorial.

@me-andre I believe we understand your position and your line of argument well now. Thank you! You may be right that the API is already as good as it will get, but there is also the possibility that it can be improved.

Can someone make a case for changing the API? eg. providing comparative analysis of various options (add componentDidReceiveProps vs merge / simplify APIs vs no change)

The React team has been watching the issue, and we do discuss it internally. In principal, I always lean toward eliminating API surface area, so I find myself leaning toward @me-andre's arguments. However, our team sometimes affectionately refers to componentDidReceiveProps as "the missing lifecycle method", and there are serious discussions about potentially adding it. The important thing is that we enable best practices without catalyzing bad practices.

What would be most useful for us (or at least me) is to have a solid understanding of WHY people want this lifecycle method. What are you trying to do in this lifecycle that isn't sufficiently covered by the other lifecycle methods? Why would someone want to do UserActions.load(id); within componentDidReceiveProps instead of within render as suggested in https://github.com/facebook/react/issues/3279#issuecomment-163875454 (I can think of a reason, but I'm curious what your reasons are)? Are there any use cases other than initiating a data-load based on props?

@jimfb I believe that componentDidReceiveProps is exactly what I need and that other methods are inappropriate, but I could be wrong. I'll happily explain my use-case.

I have a route in my react-router that looks like this:

    <Route path="/profile/:username" component={ProfilePage} />

I need to fetch the data for the profile from an external source, and in order to do this properly, I need to make an HTTP request in the componentDidMount method.

Unfortunately, when I navigate from one profile to another, React Router doesn't call the constructor method or the componentDidMount method again (of course this could be a bug but I'm going to assume it's not for now).

I thought of fixing this by using the theoretical componentDidReceiveProps. Unfortunately it does not yet exist, and componentWillReceiveProps will not serve my needs.

Any pointers on this would be greatly appreciated.

My hunch is that componentDidReceiveProps is exactly what I need.

@shea256 Can you elaborate on why componentWillReceiveProps does not serve your needs?

@shea256 Also, why does your component need to make an HTTP request? What does that http request contain? If data, why aren't you updating your component asynchronously when the data callback returns?

@jimfb The common case for me is when we have to load something asynchronously in response to a prop change, which involves setting some state to indicate that loading is happening and then when the data is loaded, setting that state.

Both mounting and receiving new props should trigger this same loading cycle, so componentDidMount and componentWillReceiveProps is the place to call the update function. render doesn't have info on whether it's a new prop, and anyway, setState from render is a no-no.

So, I have one function that does the loading. But I have to pass in props as a parameter and carefully avoid using this.props which are out of date for componentWillReceiveProps. This inevitably ends up causing bugs as you have to remember to use the passed in props or you get subtle one-behind errors when changes arrive. And the signatures of all the methods involved are more complex as the props need to be passed in.

Yes, it can be done with the current api. But it causes clumsier, error-prone code which is what React is so good at avoiding in general.

@jimfb I need to get the new username in the routeParam and with componentWillReceiveProps I don't yet have it.

I need to make an HTTP request in order to load the profile data from an external source (the data isn't stored locally).

And I can update my component in the render method but it feels a bit dirty:

constructor(props) {
  super(props)

  this.state = {
    id: this.props.routeParams.id,
    profile: {}
  }
}

componentDidMount() {
  this.getProfile(this.state.id)
}

render() {
  if (this.props.routeParams.id !== this.state.id) {
    this.getProfile(this.props.routeParams.id)
  }

  return (
    <div>
      <div className="name">
       {this.state.profile.name}
      </div>
    </div>
  )
}

@grassick wrote:

The common case for me is when we have to load something asynchronously in response to a prop change, which involves setting some state to indicate that loading is happening and then when the data is loaded, setting that state.

Yes, this is exactly my use-case.

Both mounting and receiving new props should trigger this same loading cycle, so componentDidMount and componentWillReceiveProps is the place to call the update function.

Well said.

@shea256 can't this be done using componentWillReceiveProps instead?

constructor(props) {
  super(props)

  this.state = {
    id: this.props.routeParams.id,
    profile: {}
  }
}

componentDidMount() {
  this.getProfile(this.state.id)
}

componentWillReceiveProps(nextProps) {
  let { id } = nextProps.params
  if(id !== this.state.id) {
    this.getProfile(id, (profile) => {
      this.setState({ id: id, profile: profile })
    })
  }
}

render() {
  return (
    <div>
      <div className="name">
       {this.state.profile.name}
      </div>
    </div>
  )
}

@grassick wrote:

The common case for me is when we have to load something asynchronously in response to a prop change, which involves setting some state to indicate that loading is happening and then when the data is loaded, setting that state.

@grassick wrote:

render doesn't have info on whether it's a new prop, and anyway, setState from render is a no-no.

Just spitballing here: If componentDidUpdate got invoked on initial render (in addition to subsequent renders), would that solve your use case? You could check if props changed and do all your data loading in componentDidUpdate, right? And in render, you would know you were loading data if this.state.profileName != this.props.profileName. Would that alternative be sufficient for your use cases?


Are there any use cases that people have which do NOT involve components loading data based on props?

@calmdev hm, I believe you're right. I'll try that out.

@jimfb perhaps, although I tried using componentDidUpdate and I thought it didn't work. I can take another look.

And it sounds like I totally could do a check on this.state.profileName != this.props.profileName but this also seems like a hack, doesn't it? At this point, if componentWillReceiveProps(nextProps) ends up working, I'd just prefer that. Now, what bothers me is the lack of symmetry with componentDidMount. Could I use componentWillMount in lieu of componentDidMount?

I just feel like the whole lifecycle could be cleaner.

@shea256 , I have a question for you... have you read the README for React?
I don't feel comfortable saying that but if not, you possibly shouldn't request new features for the tool you're not familiar with. For me that even sounds... absurd.
"Could I use componentWillMount in lieu of componentDidMount?"
No you can't because as it's stated in the readme, componentWillMount is called before your component gets to the DOM and componentDidMount - after that. Well, you definitely can replace one method with another and that would just break your code. The reason we have 2 methods here is not aesthetics (read "symmetry"). It's because we may need one method to do a preparation before we render and the other for some initial DOM querying / manipulation after the 1st render. But even that is exotic for an average React component. Normally, you just don't need to access the DOM manually.
"And it sounds like I totally could do a check on this.state.profileName != this.props.profileName but this also seems like a hack, doesn't it?"
Yes, your entire approach is a hack. And who told you componentDidReceiveProps would guarantee the props have been changed? Nothing will ever do that unless you define shouldComponentUpdate.
This is just the way React works.

@me-andre thanks for jumping in but you're being a bit too abrasive for my tastes. Also I don't believe you understood my questions. I'm quite familiar with what componentWillMount and componentDidMount do. I'll wait for @jimfb's response.

@shea256 ,
"Also I don't believe you understood my questions."
Could you please point at where I miss the point when I answer your questions?
Also, could you please clarify what you're trying to ask.
Also, we're here not to discuss personalities or tastes. This is not a private talk, neither it's a tech support. This is not even a stack exchange website where you could expect one to guide you through some area of knowledge.
I often talk at local meet-ups as well as international conferences about React (and not only) and I'm very open to knowledge sharing - when and where it's appropriate. You could always contact me directly via email or skype.

Regarding your issue with loading profile data. You're trying to solve the problem of applying classic imperative approach to a functional-oriented framework. A view in React is a function of props, state and environment. Like function(state, props) { return // whatever you've computed from it } (but things get slightly more complex in real world - otherwise React wouldn't exist at all). Although in 0.14 we get pure functional components, for most components this entry function is render().

It means you start writing from render() and you assume your props are always up to date and you don't care whether props have been changed or not, how many times and where. You case could be implemented the following way:

// profile-view.js

var React = require('react');

module.exports = React.createClass({
    contextTypes: {
        app: React.PropTypes.object.isRequired
        // another option is var app = require('app')
        // or even var profiles = require('stores/profiles')
    },
    componentWillMount() {
        var {app} = this.context; // another option is to require('app')
        app.profiles.addListener('change', this.onStoreChange);
    },
    componentWillUnmount() {
        var {app} = this.context; // another option is to require('app')
        app.profiles.removeChangeListener('change', this.onStoreChange);
    },
    onStoreChange() {
        this.forceUpdate();
    },
    render() {
        var {app} = this.context;
        var profile = app.profiles.read(this.props.routeParams.id);
        if (profile) { // profile's been loaded
            return <div>{profile.name}</div>;
        } else { // not yet
            return <div>Loading...</div>;
        }
    }
});

// profiles-store.js
// app.profiles = new Profiles() on app initialization

var EventEmitter = require('events');
var {inherits} = require('util');

module.exports = Profiles;

function Profiles() {
    this.profiles = {};
}

inherits(Profiles, EventEmitter);

Profiles.prototype.read = function(id) {
    var profile = this.profiles[id];
    if (!profile) {
        $.get(`profiles/${id}`).then(profile => {
            this.profiles[id] = profile;
            this.emit('change');
        });
    }
    return profile;
};

Pretty simple.
And you don't need componentDidReceiveProps. You don't even need componentWillReceiveProps and similar hooks. If you ever feel like you need them for a trivial case, you're missing understanding of how to do things in React. In order to get it please use appropriate resources, don't just trash Issues section of the repository. It feels a bit too abrasive for my tastes. It even feels like you don't respect others' time.

@me-andre It's probably worth toning down your language a bit, as it can come across as a bit confrontational even though you're just trying to help. We want to create a welcoming community for everyone; we were all newbies at one point. Even though you are correct about some of your points on the API/design, the very fact that so many people are +1'ing the issue is an indication that something is amiss, so we should try to understand what/why people want this lifecycle. Maybe the problem is that we're not sufficiently communicating how to properly write components (documentation), or maybe React's API needs a change - either way, it's worth understanding the complaints/questions/comments here.

@shea256 this.state.profileName != this.props.profileName is checking to see if the internal state (what the component is rendering) matches what the parent is asking the component to render. This is almost the definition of "component is updating" or "component is up-to-date", so I don't see it as hacky. At least, no more hacky than the idea of "fire a data-update request when the prop changes and do a setstate in the callback" is in the first place.

@shea256 To be clear: this proposed lifecycle wouldn't enable you to do anything that you couldn't already do today with the current lifecycles. It would merely make it potentially more convenient. As others have mentioned, what you are trying to do is possible with the current lifecycles (there are multiple combinations of lifecycles that would allow you to achieve your goal). If you're trying to "make it work", then StackOverflow would be a better place to discuss. This github issue is attempting to understand the need for componentDidReceiveProps as it relates to developer ergonomics.

And who told you componentDidReceiveProps would guarantee the props have been changed?

@me-andre is correct here. Most of the lifecycle methods don't actually guarantee that something has changed; they only indicate that something _might_ have changed. For this reason, you always need to check if previous === next if you are going to do something like make an http request. A bunch of people in this thread seem to be making the assumption that their value has changed merely because the lifecycle method fired.

@me-andre wrote:

You're trying to solve the problem of applying classic imperative approach to a functional-oriented framework.

I think this might be the root cause of people wanting this new lifecycle method, but I'm still trying to figure out why/how people want to use this method. It's entirely possible that the current lifecycle semantics are slightly miss-aligned with what people commonly want to do.

All: Are there any use cases for componentDidReceiveProps other than "loading data asynchronously in response to a prop change"?

cc @grassick @iammerrick

@jimfb I searched my code and I also use componentWillReceiveProps to build expensive-to-create objects that are needed for render. This case suffers from the same problem that it has to pass props and be careful not to use this.props in the code.

@jimfb wrote:

@me-andre It's probably worth toning down your language a bit, as it can come across as a bit confrontational even though you're just trying to help. We want to create a welcoming community for everyone; we were all newbies at one point. Even though you are correct about some of your points on the API/design, the very fact that so many people are +1'ing the issue is an indication that something is amiss, so we should try to understand what/why people want this lifecycle. Maybe the problem is that we're not sufficiently communicating how to properly write components (documentation), or maybe React's API needs a change - either way, it's worth understanding the complaints/questions/comments here.

Thank you for mentioning this. It's very important to make sure you have a welcoming community and my communications with you have only been pleasant.

@jimfb wrote:

@shea256 this.state.profileName != this.props.profileName is checking to see if the internal state (what the component is rendering) matches what the parent is asking the component to render. This is almost the definition of "component is updating" or "component is up-to-date", so I don't see it as hacky. At least, no more hacky than the idea of "fire a data-update request when the prop changes and do a setstate in the callback" is in the first place.

Yes, this does seem reasonable.

@jimfb wrote:

@shea256 To be clear: this proposed lifecycle wouldn't enable you to do anything that you couldn't already do today with the current lifecycles. It would merely make it potentially more convenient. As others have mentioned, what you are trying to do is possible with the current lifecycles (there are multiple combinations of lifecycles that would allow you to achieve your goal). If you're trying to "make it work", then StackOverflow would be a better place to discuss. This github issue is attempting to understand the need for componentDidReceiveProps as it relates to developer ergonomics.

I very much agree with this. I didn't post my particular use-case to receive help on it. I posted to provide some insight into why I thought componentDidReceiveProps would be useful.

I and over a dozen other people who posted clearly had an instinct to use something like this (which means that there are likely hundreds or thousands more), which indicates to me that the API is not as intuitive as it could be.

@jimfb wrote:

@me-andre is correct here. Most of the lifecycle methods don't actually guarantee that something has changed; they only indicate that something might have changed. For this reason, you always need to check if previous === next if you are going to do something like make an http request. A bunch of people in this thread seem to be making the assumption that their value has changed merely because the lifecycle method fired.

I am not making this assumption. I did leave out the check but I am now using one. But in the worst case, an additional request would be triggered.

@jimfb wrote:

I think this might be the root cause of people wanting this new lifecycle method, but I'm still trying to figure out why/how people want to use this method. It's entirely possible that the current lifecycle semantics are slightly miss-aligned with what people commonly want to do.

Perhaps. I'll think more about this.

componentDidReceiveProps would be helpful for a specific use case I have.

I'm using ReactRouter and Flux architecture. When my component is instantiated, I set my state to an item from a store. When that store emits a change event, I update my state using the same retrieval query.

When my props change because I navigated to a different item ID in the same route, I need to query my store again or my component will be stuck with state from the previous item in the store.

Currently, when componentWillReceiveProps is called, I check to see if any of my route params changed, and if they did, I call updateItemState. But, because the props have not actually changed yet, I now must pass props into my method.

this.updateItemState( nextProps );

updateItemState( props ) {
    props = props || this.props;

    this.setState( {
        item: this.getItemState( props )
    } );
}

getItemState( props ) {
    props = props || this.props;

    return this.ItemStore.get( props.params[ this.paramName ] );
}

would simply become

this.updateItemState();

updateItemState() {
    this.setState( {
        item: this.getItemState()
    } );
}

getItemState() {
    return this.ItemStore.get( this.props.params[ this.paramName ] );
}

I feel that this is a reasonable use case.

@akinnee-gl et al: If componentDidUpdate got fired after initial mount in addition to updates, would that solve your use case?

@akinnee-gl , if you use Flux, then you don't need to set the state from a store. Unless it's absolutely impossible, you should maintain the state in one place. When it comes to Flux that place is the store itself. When the store emits change, you just forceUpdate() and then in render() you read() your store.

"When my props change because I navigated to a different item ID in the same route, I need to query my store again or my component will be stuck with state from the previous item in the store."

Never.

Take a look: you need to render a particular item from a store and which item to render you decide from props. If that is your requirement, it should be expressed in the code in the same way as in words:

    var item = this.props.store.read(this.props.id);
    return <div>{item.name}</div>;

This is your component, nothing more.
In order to make this play with Flux, you can write a reusable component for store binding/unbinding:

<FluxWrapper store={store} component={Component} id={this.props.routeParams.id} />

var FluxWrapper = React.createClass({
    componentWillMount() {
        this.props.store.addListener('change', this.onStoreChange);
    },
    componentWillUnmount() {
        this.props.store.removeListener('change', this.onStoreChange);
    },
    onStoreChange() {
        this.forceUpdate();
    },
    render() {
        var Component = this.props.component;
        return <Component {...this.props} />;
    }
});

var Component = React.createClass({
    render() {
        var item = this.props.store.read(this.props.id);
        return <div>{item.name}</div>;
    }
});

See how tiny your components can become if you use React properly.

However if your stores are heavy and you can't just read() them on every render(), then you need a caching middleware. This can be either a reusable component (similar to FluxWrapper) or an intermediate proxy store which shadows out the original read() method. Proxy stores are easy and cool - they can not only cache reads but also suppress changes if a parent store change wasn't important or significant for the case.
This is called composition. You should prefer composition over inheritance or extending functionality or whatever because composition scales.
As soon as you've got an issue which is hard to solve by using one component, please consider using 2 or more components instead of introducing complexity to existing components.

@jimfb , regarding my tone: I'm not a native English speaker and don't even have a regular practice of speaking, so I may sometimes pick the wrong words - I just don't feel how they sound emotionally.

Interesting approach. I will keep it in mind. All of the official docs for React say to keep your render functions pure (meaning that you should only access props and state in the render methods). All of the Flux examples show setting state from the store.

@jimfb The problem with componentDidUpdate is that it is called for other reason than props changing. Also, it would require me to wait until the component has already updated, then call setState to update it again! Seems pretty inefficient.

@akinnee-gl componentWillReceiveProps may also be called for reasons other than props changing (see: https://github.com/facebook/react/issues/3279#issuecomment-164713518), so you MUST check to see if the prop actually changed anyway (regardless of which lifecycle you choose). Also, if you're actually doing asynchronous data, you're going to be waiting for the next event loop before your callback will get fired anyway, so I don't think it would matter if your function got invoked immediately before/after your render function, right?

@jimfb Well actually, if the item the prop is referencing is already in the store, there is no need to render again. If it's not in the store yet, we may want to show a loading screen. So it still doesn't make sense to render twice. I see what you're saying though.

@me-andre I really like your approach of just reading out of the store during render. It does seem to simplify the code. I worry that you lose some efficiency by doing that though. You lose the ability to control updating via shouldComponentUpdate, and it requires you to add that extra FluxWrapper component that you mentioned. Any thoughts on that?

I've been monitoring the discussion and I'd like to resurface the third option: (1) API is great as is, (2) it needs componentDidReceiveProps, and (3) maybe there is a simpler API.

I'm looking at issue as an opportunity to see if we can solve the underlying need raised by this issue by brainstorming more deep changes the API rather than limiting the discussion to the solution space from (1) and (2).

A simplified API could be based on the following principles:

(A) simplify code structure / reduce boilerplate
(B) keep props (inputs) separate from state (internal)

The main reason for (A) is that I find myself writing non value-adding boilerplate. Example:

componentWillMount() { this.actuallyCheckThePropsAndMaybeDoSomething(); }
componentWillReceiveProps(nextProps) { this.actuallyCheckThePropsAndMaybeDoSomething(nextProps); }

actuallyCheckThePropsAndMaybeDoSomething(props) {
  props = props || this.props;

  let relatedProps1 = _.pick(props, KEYS1);
  if (!shallowEqual(this.relatedProps1, relatedProps1) { // changed
   this.relatedProps1 = relatedProps1;

   // do something
  }

  let relatedProps2 = _.pick(props, KEYS2);
  if (!shallowEqual(this.relatedProps1, relatedProps2) { // changed
   this.relatedProps2 = relatedProps2;

   // do something else
  }
}

I would really rather do the following and not have a separate code path for first time vs changed:

propsUpdated(prevProps) {
  if (!shallowEqual(_.pick(prevProps || {}, KEYS1), _.pick(this.props, KEYS1)) { // changed
   // do something
  }

  if (!shallowEqual(_.pick(prevProps || {}, KEYS2), _.pick(this.props, KEYS2)) { // changed
   // do something
  }
}

As for (B), the main problem with the componentDidUpdate is like was just mentioned is that you can trigger repeated method calls if you set state due to property changes since it is called for both props (inputs) and (internal) state. These code paths seems to be better as decoupled because props are supplied from the outside and state is set in the inside, eg. I've tried / considered a fourth possibility of updated(prevProps, prevState) (a simplified name for componentDidUpdate since fewer lifecycle methods could allow for shorter naming) using componentDidUpdate to reduce boilerplate, but I find myself a bit dissatisfied with the potential redundant second update calls and that the logic seems to be quite independent in practice.

Based on starting with the design principles (I'm sure there are more!), I'm wondering if something along the lines of propsUpdated and stateUpdated could be a potential third option for this discussion?

@kmalakoff You will always need to check if the props changed, since we will never be able to tell you definitively that props did/didn't change (because Javascript does not have value-types and does have mutability).

On that note, perhaps it could call shouldComponentUpdate to tell if it should call propsDidChange or whatever. Separate issue though.

@jimfb not following precisely what the language limitation is that you are referring to and how it relates to simplifying the API in general. Can you please explain in a little more detail or with an example? I need this explanation simplified / expanded a little to understand...

I think he means that there's not a built in, fast way to check if this.props === nextProps because they may or may not be two separate objects.

{ a: 1 } === { a: 1 } yields false because they are two separate objects, but

var a = { a: 1 };
var b = a;
a === b

yields true because they are actually both a reference to the same object.

We could recursively check each property for equality, but that could be pretty slow. That's why it's up to us to implement shouldComponentUpdate

@kmalakoff I don't want to digress this thread, but here is a gist for you: https://gist.github.com/jimfb/9ef9b46741efbb949744

TLDR: @akinnee-gl is exactly correct in his explanation (Thanks!). With the minor correction that we couldn't always do the recursive check anyway (even if we wanted to, and performance were a non-issue), because there is no way to recursively check a callback function passed in as a prop.

Let's keep this thread on-topic though :P.

Thank you both! Hmmm, still not totally clear on why simplifying the API is not an option to resolve this issue. I'll add comments to the gist...

If anyone wants to participate in a wider range of solutions, please join us there!

@jimfb @calmdev I've tried your suggestions and I now have a full grasp on why componentDidReceiveProps doesn't really add anything new and should not be added as an additional function. While I trusted your word earlier, it now makes intuitive sense to me why this is the case.

To illustrate how I came to this realization, I'd like to share my newest code. Perhaps this will be able to help others as well.

Take a look at my profile page component:

class ProfilePage extends Component {
  static propTypes = {
    fetchCurrentProfile: PropTypes.func.isRequired,
    currentProfile: PropTypes.object.isRequired
  }

  constructor(props) {
    super(props)
    this.state = { currentProfile: {} }
  }

  componentHasNewRouteParams(routeParams) {
    this.props.fetchCurrentProfile(routeParams.id)
  }

  componentWillMount() {
    this.componentHasNewRouteParams(this.props.routeParams)
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.routeParams.id !== this.props.routeParams.id) {
      this.componentHasNewRouteParams(nextProps.routeParams)
    }
    this.setState({
      currentProfile: nextProps.currentProfile
    })
  }

  render() {
    var profile = this.state.currentProfile
    return (
      <div>
        <h1>{ profile.name ? profile.name : null }</h1>
      </div>
    )
  }
}

When the component is mounted, it has no profile data and needs to load the profile from an external source.

So... I have it call the fetchCurrentProfile function (wrapped in componentHasNewIdProp) which fetches the data from the external source, then dispatch an update action to Redux. The reducer receives this action and updates the application state, which then updates the props for the ProfilePage component.

Now, since the props have been updated, the componentWillReceiveProps function is called. But we don't know the context of the prop change event, so we need to figure out which props (if any) have been changed. We see that the ID is the same, so we don't need to call fetchCurrentProfile again. Then we update state.currentProfile so that the component knows to re-render with the new profile data (yes I could do this with props, but there's another reason for this that I don't want to go into here). At this time, we could check to see if props.currentProfile has changed before updating the state, but this isn't an expensive operation so it doesn't matter if we check.

Then... let's say we want to navigate from one profile to another. We click a link and the router triggers the route change. We now have new route parameters and the componentWillReceiveProps function gets triggered. We see that the id has changed and so we call fetchCurrentProfile again (via componentHasNewIdProp).

When the new fetch comes back and dispatches the redux action, the currentProfile prop is updated again, the state is then updated, and the component re-renders with the new view.

This example illustrates how there needs to be different behavior when the component mounts and when the component receives new props. It also demonstrates how the props have to be checked for changes in the latter case and how certain functions should be called only upon certain prop changes.

Looking back, essentially what I wanted was a function that only updated when the routeParams changed. However, there is no need to add another function because we can do everything we want with existing functions, plus we don't want to complicate the lifecycle.

Something that might help here, though, is more documentation on the lifecycle, working examples, etc.

Thanks for your help guys. Please let me know if there's anything I got wrong here. I hope this will be helpful for anyone who stumbles upon this thread in the future.

@shea256 your example seems like a reasonable case for not needing a new API from a being able to achieve what you need without a new API standpoint.

I outlined more thoughts in: https://gist.github.com/jimfb/9ef9b46741efbb949744, but I still feel like we should be able to streamline the API to write less code...

Imagine that the api allowed you to write the same functionality, but with less boilerplate:

class ProfilePage extends Component {
  static propTypes = {
    fetchCurrentProfile: PropTypes.func.isRequired,
    currentProfile: PropTypes.object.isRequired
  }
  state = {currentProfile: {}}; // not material to example (babel-stage-1)

  // called both on init and update
  willReceiveProps(nextProps) {
    if (!this.props || (this.props.routeParams.id !== nextProps.routeParams.id)
      nextProps.fetchCurrentProfile(nextProps.routeParams.id)
  }

  render() {
    var profile = this.props.currentProfile;
    return (
      <div>
        <h1>{ profile.name ? profile.name : null }</h1>
      </div>
    )
  }
}

The idea that i'm hoping will be considered is reducing the API surface area to cut down on the number of code paths and boilerplate. I'm wondering if the discussion is anchoring on the initial request to add something when maybe looking at it from a different perspective would open new paths.

This is just one option (my preference is receivedProps(prevProps) because ideally, there would be only one prop-related method where you could use this.props), but the important part is that there is one code path for the initialization of the component and any prop changes rather than the different code paths that we have now with different API signatures. Whatever solution people agree to be better.

Getting a reduction in boilerplate is what I'm hoping this issue can achieve (to clarify my need / problem!) and I'd like to brainstorm a little more widely on what that solution could look like (consider taking APIs away, changing the lifecycle methods significantly, shortening method names now that ES6 is widely available and Components seem to be the way forward, etc).

@akinnee-gl , having an application composed of many tiny components is much better than of a few heavy components for a several reasons:

  1. This approach connects with "single responsibility principle"
  2. You can clearly see which component does what
  3. You can easily compose features and extend components by wrapping
  4. Methods and properties do not collide
  5. It's easier to extend a feature because one component encapsulates one feature

Regarding your notice that reading from a store on each render may be inefficient, I've made a gist which shows a basic idea how to implement a middleware component for caching: https://gist.github.com/me-andre/0ca8b80239cd4624e6fc

I've taken another pass at the principles / goals that could lead to an improved API (hopefully based around a received props method as suggested in this issue):

(A) simplify code structure / reduce boilerplate
(B) keep props (inputs) separate from state (internal)
(C) allow for a method to control the rendering of a component
(D) simplify method naming based on the assumption of Component subclassing
(E) reduce the number of props methods to one for both initialization and changes
(F) the props method should have this.props with the latest value

There are clarifications on some of them in the gist.

@kmalakoff As per the gist, let's not increase the scope of these issues. The issue is already complex enough, and digressing the conversation into a full API-redesign is not going to be tractable. You're welcomed to continue thinking about these issues, but please keep this thread on topic.

@kmalakoff Many of the points you're mentioning are topics of other issues. For instance (D) is answered by https://github.com/reactjs/react-future/issues/40#issuecomment-142442124 and https://github.com/reactjs/react-future/issues/40#issuecomment-153440651. Those are the appropriate threads to have that discussion. If you're looking for more general/holistic design/API discussions, the correct medium is probably https://discuss.reactjs.org/

@jimfb I've given some thought to this after you suggested I move this to the gist and actually do not believe this line of analysis is off topic...the reason I am interested in this issue around received props is that I believe having a received props lifecycle method will allow me to reduce boilerplate.

Please hear me...reducing boilerplate is the problem that the lifecycle method in this issue could solve for me.

I believe that it also could be a significant part of the reason why people are interested in this lifecycle method because the current API encourages boilerplate more than we would like.

That said, I'd be happy to try to decouple the API improvements and for example, only focus on exploring the solution space around improving the API related to props in this issue.

@kmalakoff The topic of the thread is defined by the title and first post. The title is componentDidReceiveProps Please not lots of general ways to reduce boilerplate. This thread is about reducing boilerplate specifically through the introduction of a very specific lifecycle method. That discussion alone is already nuanced enough, without introducing additional topics (like renaming all the functions) that are already being discussed in other issues.

Your points are valuable, and I encourage you to have the discussion, just please move it to a different thread, because we want to keep github threads focused on the topic at hand. Otherwise the discussions are too difficult to track/manage. Threads do exist for most of the topics you raised.

@jimfb why are people asking for componentDidReceiveProps? The reason I am interested in it is to reduce boilerplate related to props. I'm not sure how state this more clearly so I can be heard and understood.

You have asked for the problems and use cases, and I've stated this as the problem for me and shown a use case above (responding to @shea256) which seem on-topic, within scope and, from my perspective, important for resolving this issue.

I have agreed to hold off on expanding the scope to general API issues / improvements and will. I promise! :wink:

FYI: as I stated previously, the reason I started on the thought experiment around this issue was because the arguments seemed a little too narrowly focussed (eg. we can already do everything we need with the current API) rather identifying the underlying motivations behind the issue. As you know, sometimes you need to step back, then focus in, then view from a different angle, then revisit assumptions, investigate the underlying reasons behind the request, etc to iterate towards the best solution to a problem. Hopefully, I have helped draw attention to this and my participation will help get this issue resolved to our satisfaction! (of course, I'm also hoping the React API will evolve to be more use-friendly and will continue that discussion elsewhere...thank you for the links)

@kmalakoff , ok, to reduce boilerplate you just make reusable classes or components from which you subclass or compose another components. Nobody prevents you from defining a common method for all components in your app. React does provide excellent extendability: you can define components as objects, functions or classes. You can extend components by subclassing, mixins or factories.

@me-andre that absolutely is an option if this issue does not lead to an API improvement.

This issue was raised to promote discussion around a desire to make a change to the API to improve it. Exploring alternatives in client code definitively should be considered, but if a strong case is made that the API should be changed, these sort of workarounds would be unnecessary. In order to counter argue, cases also need to be made for what an improved API should be and then evaluated against the current API to assess them.

For example, if you were to demonstrate that the current API without defining a custom superclass could be used in a way to reduce boilerplate (eg. we are using the API incorrectly, or there are features in the current API that can be used to achieve a similar level of reduction of boilerplate, etc) or prove why there is no improvement possible (eg. no general solutions are exist because there is a major use case that could not be supported in our API improvement options), etc, you could make a case that the API is good enough as is.

If the API requires people to write custom superclasses for basic and common control flow patterns, it strengthens the argument that there is a case for the API to be improved.

Curious - what is the rationale for not having componentWillReceiveProps triggered before mounting? If you think about it, the component is really receiving props when it is initialized. It's just not receiving new props.

What are the use-cases where you would only want to trigger something upon receiving new props (and not on the initial prop reception)?

I could be missing something obvious here but just trying to reconcile the viewpoints of the various parties.

In any case, if componentWillReceiveNewProps were important in some cases, one could still simulate it with a modified componentWillReceiveProps by performing an check on the props coming in.

@kmalakoff if componentWillReceiveProps triggered the first time, would that meet your standards for API simplification?

componentWillReceiveProps not being triggered on mount is not the reason people are asking for componentDidReceiveProps. People are asking for componentDidReceiveProps because they wrote all of their methods to access this.props. When componentWillReceiveProps is called, the nextProps are passed, but this.props has not changed yet, meaning we have to pass nextProps into all of the methods that are called in response to componentWillReceiveProps, instead of leaving them as-is. We end up with a ton of props = props || this.props and a ton of passing props into every function we call.

@shea256 good points. Having different code paths for initialization vs changes is one of the root causes of prop boilerplate. The other root cause is having with different signatures for handling props like @akinnee-gl points out.

This is why I'm trying to expand the solution space being considered (eg. also call on init) since there could actually be an even better solution to reduce prop boilerplate more.

I hope we will be able to get further:

Before New Hook

componentWillMount() {
  this.setup(this.props.id);
}

componentWillReceiveProps(next) {
  this.setup(next.id);
}

setup(id) {
  UserActions.load(id);
}

After New Hook (Revised)

componentDidReceiveProps(prevProps) {
  UserActions.load(this.props.id);
}

or if UserActions.load cannot check the currently loaded id:

componentDidReceiveProps(prevProps) {
  if (!prevProps || (prevProps.id !== this.props.id))
    UserActions.load(this.props.id);
}

@kmalakoff , what I'm talking about is that API improvement is absolutely available for you right now: you can make your own factory of components and then share with us (along with use case samples). This will make your proposal and rationale hundred times more clear. Since all lifecycle points already have appropriate callbacks, you can introduce any new method at any lifecycle point / component state. You can even mixin an event emitter into your component and make it possible to attach several handlers for a state change.

@shea256 , one possible reason for componentWillReceiveProps not to get triggered before first render is that there's no such thing as this.props at that time. What you commonly do in componentWillReceiveProps is comparing this.props[propName] with newProps[propName]. Having the method triggered before 1st render would make you also have to check for presence of this.props. Moreover, the whole component is completely uninitialized at the time it receives props before the initial render, it doesn't even have state.

@kmalakoff , I've twice posted code samples which show how to organize a React component in a way that it doesn't need any setup or similar hacks. Could you please tell why do you still strive to change the behavior of a React component instead of adjusting your code so that it integrates with React? It would be great if you just point at where my samples are inappropriate for your use-case.

@akinnee-gl , the reason not to introduce a new method for just accessing this.props on update is that we have such method - it's called render(). It even gets called after a check for shouldComponentUpdate() - which is normally a place where you do this.props !== newProps or _.isEqual(this.props, newProps) etc.
If you feel like you should have a separate method for some setup, why don't just subclass a React component and define a following render() method

this.setup(this.props);
return this._render();

I just don't see how it simplifies things in the React ecosystem, but that is what you keep requesting.

@me-andre the premise of this issue is not that we cannot achieve what we want with the current API nor that we cannot work around the current API in client code. The premise of this issue is that the current React API is sub-optimal and needs to be improved; for example, if you were to come up with principles of what an optimal API would look (like I tried to above), the current React API would score in a mid-level range because it is suboptimal / deficient in a number of areas.

Unfortunately, providing ways in client code to work around the React API does not address the root causes of the problem, shifts the debate away from addressing the underlying the issues, and will not lead to a debate around potential solutions to improve the React API.

In short, I already have workarounds that are working for me because I have a bunch of React apps in production so best practices could be great for a blog post, but using them as a line of debate in this issue will just sidetrack us from a real debate on the true purpose of this issue: how to improve the React API (in this issue, limited to prop use cases and boilerplate).

React 1.0 should aim for the top, not the middle. A major version increase can break backwards compatibility so let's go for the best possible API based what we've learned from all of these years of using the 0.x version.

(FYI: I think people might not be engaging with your examples as much as you are hoping because they are not coming here to get taught about the current API, but because they are looking for / hoping for improvements to the API, eg. they could be perceived as with good intentions, but being slightly misaligned)

I think people might not be engaging with your examples as much as you are hoping because they are not coming here to get taught about the current API, but because they are looking for / hoping for improvements to the API

Ok, you come with an API improvement proposal, but then you show the code which is way too far from "React best practices". Some things seem very much like worst practices. Then you tell: "that is the reason for a change". Yes, that is the reason for a change but not in the React codebase.
I show you how to reorganize the code to make it work well with React but just ignore proper usage showcase and keep insisting. That doesn't seem to be a constructive debate.

Unfortunately, providing ways in client code to work around the React API does not address the root causes of the problem

When you wrap a low-level API with a higher level API that is not a workaround, but a common practice. Many brilliant frameworks follow that idea.
What I keep telling is wrap the existing ugly API with what you would like to use and share with us. Along with the examples of use and explanations why it got better it would be incredible.
Again, I just don't see a reason why don't you just do that. Because if what you're talking about is a common problem, that would be a great help for many people.

I have a bunch of React apps in production so best practices could be great for a blog post, but using them as a line of debate in this issue will just sidetrack us from a real debate

What you commonly do when you architect/design an API is predicting problems, hypothesizing about use-cases etc. The reason why people hypothesize is not that they try to abstract themselves from a real world in search of the ideal. It's just lack of experience - you can't get experience "in advance".

You tell you have that experience, you've seen real problems and you have some workarounds you can share. That is exactly what can turn this debate into being "real" and "effective". The React itself was built on real problems and challenges - that's why it solves real architecture problems and not just "how to write hello world in 3 lines".

@me-andre I hear you and your line of argument is clear.

You are correct that central to my argument is that if we establish better principles based on our collective experiences of using the current React API and open up debate to include non-dogmatic solutions that may change the API in some fundamental ways, we can and should seize the opportunity to improve the React API to make it even better than it is today. Let's not rest on our laurels when there is room to improve!

How would you rate the current React API around props? (let's say using letter grades: F to A+), why, and what would you do to improve it if it is less than an A+?

@me-andre have you had a chance to prepare your ranking and analysis? I'm interested in hearing what you believe are the strengths, weaknesses, and opportunities with the current API.

+1

+1 Please

Is there a workaround? I need ComponentDidReceiveProps

I made this issue over a year ago and have been using React daily since. I no longer think there is a use case for componentDidReceiveProps that justifies increasing the API.

@AlexCppns what is it you are trying to do?

@iammerrick, actually it's alright, I just misunderstood how componentWillReceiveProps is used.

I've ran into this a few times, and what I ended up doing was:

componentWillReceiveProps(nextProps) {
  if (this.props.foo !== nextProps.foo) this.needsUpdate = true;
}
componentDidUpdate() {
  if (this.needsUpdate) {
    this.needsUpdate = false;
    // update the dom
  }
}

It's not too bad.

@brigand , there's no need for a flag - you can compare props within componentDidUpdate():

componentDidUpdate(prevProps) {
    let needsUpdate = prevProps.foo !== this.props.foo;
    // ...whatever
}

Furthermore, you solution could be easily broken by shouldComponentUpdate(), which may prevent re-rendering.

Oh cool, thanks!

@jimfb I think I have a synchronous use case below. I think a componetDidReceiveProps would've been perfect for this.

  componentDidMount() {
    this._selectAll()
  }

  componentDidUpdate(prevProps) {
    let shouldUpdateSelected = (prevProps.recordTypeFilter !== this.props.recordTypeFilter) ||
      (prevProps.statusFilter !== this.props.statusFilter) ||
      (prevProps.list !== this.props.list)

    if (shouldUpdateSelected) { this._selectAll() }
  }

  _selectAll() {
    this.setState({ selectedIds: this._getFilteredOrders().map((order) => ( order.id )) })
  }

  _getFilteredOrders() {
    let filteredOrders = this.props.list

    // recordTypeFilter
    let recordTypeFilter = this.props.recordTypeFilter
    filteredOrders = _.filter(filteredOrders, (order) => {
        // somelogic
    })

    // statusFilter
    let statusFilter = this.props.statusFilter
    filteredOrders = _.filter(filteredOrders, (order) => {
        // somelogic
    })

    return filteredOrders
  }

@chanakasan , your example lacks render() method which is essential for both understanding your example and suggesting a better solution.
Second, your code is connected to some custom business logic and isn't easy to read. Don't hesitate to explain, not just throw a copy-paste into others.
I've read your example however, and would like to share following thoughts:

  1. This is the use case of componentWillReceiveProps, not componentDidUpdate. If you switch to componentWillReceiveProps, your component would re-render twice as less while preserving the same logic. After a year since I left this discussion I still see zero use case for componentDidUpdate except for reacting to DOM changes.
  2. Much better, however would be avoiding hooks at all and moving all logic to the render() method / migrating to a stateless component, because this.state.selectedIds isn't actually a state. It's purely computed from props.

Hi @me-andre, Thanks for taking the time to reply. I've read the discussion on this page and I like to thank you for participating in it.

Yes, a componentDidReceiveProps is not needed, but things seems mysterious without it.
You've said your component would re-render twice as less if I use componentWillReceiveProps. That's still a mystery to me.

I did think and try the two things you've suggested before, but failed.

  1. componentWillReceiveProps won't work because the _getFilteredOrders() function called from _selectAll() needs the newProps.
  2. I couldn't think of a way to derive the selectedIds without storing it in state.

I just created a complete example from my use case. I've made it easy to understand much as possible.

If you could please have a look and point me in the right direction. If I am able to understand this properly I'll share this example in a blog post to help others as well.

@chanakasan, come on, this looks like a production code. I'm not gonna read all of it and help you with your project for free.

However I can point you to the right direction:

  1. Both componentWillReceiveProps() and componentDidUpdate() can access prev and next props. That's clearly seen from the official React docs.
  2. From the complete copypaste of your code it's now obvious that you use state for storing selected ids which a user can toggle. It's fine to use state then, but still componentWillReceiveProps() would trigger re-render twice as less. (because a render is gonna happen after componentWillReceiveProps() anyway, setState() would just update the state for the upcoming render).
  3. Please take a look at the docs. Save your time and respect others.

@me-andre I think I understand your point about componentWillReceiveProps using this line in the docs:

componentWillReceiveProps() is not invoked if you just call this.setState()

But the caveat of using componentWillReceiveProps is I'll have to pass along nextProps to functions.

I'll try to follow your advice. Thanks I appreciate it.

Btw, it wasn't my intention for you to help with my production project for free :). It's a more full example from my previous short example to fill in any blanks about my use case.

what if we use this in conjunction with shouldComponentUpdate?
where by we do not wish to update component state or rerender,
but we need this.props to be using latest props to do some manual script work after props is received

my work around is to set this.props to the new props before running the desired function

It really would be nice to have the ComponentDidMount or some hook for that. Why? Because, some times, we need to do other calculations which doesn't depend on React lifecycle.

For example: I have a parent component which may be resized. The child component is responsible to render an OpenLayer map which has a function that is responsible to resize the map. However, it should happen after the child get props from the parent and also committed other calculations within React lifecycle.

Was this page helpful?
0 / 5 - 0 ratings