The componentWillReceiveProps()
method only triggers if props change. However, there are situations where the context changes, but the props do not, but this method does not trigger. It makes it really difficult to alter the state before an update, as we can't do this in componentWillUpdate()
.
Perhaps a new method, like componentWillReceiveContext()
can be added, or the previous componentWillReceiveProps()
can simply be renamed to something like componentWillReceive()
, which is always called if any prop/state/context has changed.
In fact, componentWillReceiveProps
does trigger when context
changes. Here's an example fiddle of how it can be used and accomplished:
https://jsfiddle.net/bzytthat/
Read more about using context
in lifecycle methods: http://facebook.github.io/react/docs/context.html#referencing-context-in-lifecycle-methods
I have an implementation that does not work.
https://github.com/titon/toolkit/blob/3.0/src/ui/components/carousel/ItemList.js
When I add componentWillReceiveProps()
to ItemList
, it _never_ gets triggered, even when the context constantly changes from the parent. No idea why, since in your example it does. Perhaps an ES6 issue?
I am using the latest React 0.14.
Here's an example of the Carousel in use: https://github.com/titon/toolkit/blob/3.0/src/test.js
Edit: Out of curiosity, I added componentWillReceiveProps()
to Item
, which does trigger. Still no idea why ItemList
does not.
Edit 2: I removed everything from ItemList
except for a basic render method, and it still would not trigger.
I did some debugging in the React source, and it seems to always trigger true for this clause. https://github.com/facebook/react/blob/3b96650e39ddda5ba49245713ef16dbc52d25e9e/src/renderers/shared/reconciler/ReactCompositeComponent.js#L657
Which seems to agree with my original issue, that the componentWillReceiveProps()
isn't being called because the props haven't changed.
I have a perfect example of this not working correctly. I added a console log statement to every lifecycle method for each child of Carousel
. Tested with the following JSX markup.
import Carousel from './ui/components/carousel';
const log = function(e, ...args) {
console.log(e.constructor.name, e.type, e.detail, e, args);
};
<Carousel
component="slideshow" modifier="slide"
debug={true} perCycle={1} loop={true}
infinite={true} autoStart={false} pauseOnHover={false}>
<Carousel.ItemList swipe={true} onSwipe={log} onSwipeRight={log}>
<Carousel.Item index={0}>0</Carousel.Item>
<Carousel.Item index={1}>1</Carousel.Item>
<Carousel.Item index={2}>2</Carousel.Item>
<Carousel.Item index={3}>3</Carousel.Item>
<Carousel.Item index={4}>4</Carousel.Item>
<Carousel.Item index={5}>5</Carousel.Item>
<Carousel.Item index={6}>6</Carousel.Item>
<Carousel.Item index={7}>7</Carousel.Item>
<Carousel.Item index={8}>8</Carousel.Item>
</Carousel.ItemList>
<Carousel.TabList onClick={log} />
<Carousel.Next onClick={log}>Next</Carousel.Next>
<Carousel.Prev onClick={log}>Prev</Carousel.Prev>
<Carousel.Start onClick={log}>Start</Carousel.Start>
<Carousel.Stop onClick={log}>Stop</Carousel.Stop>
</Carousel>
And the console output on initial load.
ItemList componentWillMount
Item componentWillMount (x9)
TabList componentWillMount
Tab componentWillMount (x9)
Next componentWillMount
Prev componentWillMount
Start componentWillMount
Stop componentWillMount
Item componentDidMount (x9)
ItemList componentDidMount
Tab componentDidMount (x9)
TabList componentDidMount
Next componentDidMount
Prev componentDidMount
Start componentDidMount
Stop componentDidMount
--------- Updated the state for number of visual items, will render 3 items and 9 tabs ---------
ItemList shouldComponentUpdate
ItemList componentWillUpdate
Item componentWillReceiveProps
Item shouldComponentUpdate
Item componentWillUpdate
Item componentWillReceiveProps
Item shouldComponentUpdate
Item componentWillUpdate
Item componentWillReceiveProps
Item shouldComponentUpdate
Item componentWillUpdate
TabList shouldComponentUpdate
TabList componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Next shouldComponentUpdate
Next componentWillUpdate
Prev shouldComponentUpdate
Prev componentWillUpdate
Start shouldComponentUpdate
Start componentWillUpdate
Stop shouldComponentUpdate
Stop componentWillUpdate
Item componentDidUpdate (x3)
ItemList componentDidUpdate
Tab componentDidUpdate (x9)
TabList componentDidUpdate
Next componentDidUpdate
Prev componentDidUpdate
Start componentDidUpdate
Stop componentDidUpdate
The order of the events match the order of the elements in the JSX. HOWEVER, if you look closely, you'll notice that componentWillReceiveProps
only triggers for Item
and Tab
, both of which are 2nd level children. None of the first level children trigger.
And here's the output when you cycle to the next item in the list.
SyntheticMouseEvent click 1 Object { dispatchConfig: Object, dispatchMarker: ".0.2", nativeEvent: click, target: <button.carousel-next>, currentTarget: <button.carousel-next>, type: "click", eventPhase: 3, bubbles: true, cancelable: true, timeStamp: 492246832, 22 moreโฆ } Array [ ]
ItemList shouldComponentUpdate
ItemList componentWillUpdate
Item componentWillReceiveProps
Item shouldComponentUpdate
Item componentWillUpdate
Item componentWillReceiveProps
Item shouldComponentUpdate
Item componentWillUpdate
Item componentWillMount
TabList shouldComponentUpdate
TabList componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab shouldComponentUpdate
Tab componentWillUpdate
Next shouldComponentUpdate
Next componentWillUpdate
Prev shouldComponentUpdate
Prev componentWillUpdate
Start shouldComponentUpdate
Start componentWillUpdate
Stop shouldComponentUpdate
Stop componentWillUpdate
Item componentDidUpdate
Item componentDidMount
ItemList componentDidUpdate
Tab componentDidUpdate
TabList componentDidUpdate
Next componentDidUpdate
Prev componentDidUpdate
Start componentDidUpdate
Stop componentDidUpdate
Carousel#6762at6g0kk 3334.095 cycling 1
Carousel#6762at6g0kk 3334.355 cycled 1
The same thing happens again, componentWillReceiveProps
is not called for 1st level children, only 2nd level. I'll try and debug some more, but not really sure where to begin.
I tested this using my pull request here: https://github.com/facebook/react/pull/5776
ItemList componentWillMount
Item componentWillMount (x9)
TabList componentWillMount
Tab componentWillMount (x9)
Next componentWillMount
Prev componentWillMount
Start componentWillMount
Stop componentWillMount
Item componentDidMount (x9)
ItemList componentDidMount
Tab componentDidMount (x9)
TabList componentDidMount
Next componentDidMount
Prev componentDidMount
Start componentDidMount
Stop componentDidMount
--------- Updated the state for number of visual items, will render 3 items and 9 tabs ---------
ItemList componentWillReceiveContext ***
ItemList shouldComponentUpdate
ItemList componentWillUpdate
Item componentWillReceiveProps
Item componentWillReceiveContext ***
Item shouldComponentUpdate
Item componentWillUpdate
Item componentWillReceiveProps
Item componentWillReceiveContext ***
Item shouldComponentUpdate
Item componentWillUpdate
Item componentWillReceiveProps
Item componentWillReceiveContext ***
Item shouldComponentUpdate
Item componentWillUpdate
TabList componentWillReceiveContext ***
TabList shouldComponentUpdate
TabList componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Next componentWillReceiveContext ***
Next shouldComponentUpdate
Next componentWillUpdate
Prev componentWillReceiveContext ***
Prev shouldComponentUpdate
Prev componentWillUpdate
Start componentWillReceiveContext ***
Start shouldComponentUpdate
Start componentWillUpdate
Stop componentWillReceiveContext ***
Stop shouldComponentUpdate
Stop componentWillUpdate
Item componentDidUpdate (x3)
ItemList componentDidUpdate
Tab componentDidUpdate (x9)
TabList componentDidUpdate
Next componentDidUpdate
Prev componentDidUpdate
Start componentDidUpdate
Stop componentDidUpdate
And here's the output when you cycle to the next item in the list.
SyntheticMouseEvent click 1 Object { dispatchConfig: Object, dispatchMarker: ".0.2", nativeEvent: click, target: <button.carousel-next>, currentTarget: <button.carousel-next>, type: "click", eventPhase: 3, bubbles: true, cancelable: true, timeStamp: 492246832, 22 moreโฆ } Array [ ]
ItemList componentWillReceiveContext ***
ItemList shouldComponentUpdate
ItemList componentWillUpdate
Item componentWillReceiveProps
Item componentWillReceiveContext ***
Item shouldComponentUpdate
Item componentWillUpdate
Item componentWillReceiveProps
Item componentWillReceiveContext ***
Item shouldComponentUpdate
Item componentWillUpdate
Item componentWillMount
TabList componentWillReceiveContext ***
TabList shouldComponentUpdate
TabList componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Tab componentWillReceiveProps
Tab componentWillReceiveContext ***
Tab shouldComponentUpdate
Tab componentWillUpdate
Next componentWillReceiveContext ***
Next shouldComponentUpdate
Next componentWillUpdate
Prev componentWillReceiveContext ***
Prev shouldComponentUpdate
Prev componentWillUpdate
Start componentWillReceiveContext ***
Start shouldComponentUpdate
Start componentWillUpdate
Stop componentWillReceiveContext ***
Stop shouldComponentUpdate
Stop componentWillUpdate
Item componentDidUpdate
Item componentDidMount
ItemList componentDidUpdate
Tab componentDidUpdate
TabList componentDidUpdate
Next componentDidUpdate
Prev componentDidUpdate
Start componentDidUpdate
Stop componentDidUpdate
Carousel#6762at6g0kk 3334.095 cycling 1
Carousel#6762at6g0kk 3334.355 cycled 1
So far it works correctly. Still trying to find out why componentWillReceiveProps
isn't.
Fixed here: https://github.com/facebook/react/pull/5787
Most helpful comment
I did some debugging in the React source, and it seems to always trigger true for this clause. https://github.com/facebook/react/blob/3b96650e39ddda5ba49245713ef16dbc52d25e9e/src/renderers/shared/reconciler/ReactCompositeComponent.js#L657
Which seems to agree with my original issue, that the
componentWillReceiveProps()
isn't being called because the props haven't changed.