I am understanding how onPush change detection works in angular. Much of the concept I am cleared. But there is one thing which I am also finding hard to get on Google.
Scenario,
Suppose we have a parent component passing some values to child component and child component have onPush change detection strategy and that child component have a button performing some task not emitting anything to parent.
When we click on that button from child component change detection works from Root component to that parent component and then it seems that there is no reference change in child component so it should not execute the change detection in the child component from where the button was clicked because the reference is not changed.
But it executes there as well.
Here I am confused can anyone help me to make it more clear.
Related
Let's say we have a parent component and multiple functional child components. I want to clearly know if the parent re-renders does the child re-renders or not.
After going through a few articles I came to know there are 3 ways we can detect rerenders. (Let me know if there are more ways.)
1. Put a console.log in the child component.
2. Use Chrome paint flashing option in the settings.
3. Use React dev tool
Do all these ways are correct to know if the component really rerenders? Because it doesn't seem to be work correctly with React.memo.
When I am wrapping the Child component with React.memo it does not print console.log, when the parent component rerenders which is correct. But still with chrome and react dev tools highlights the child component as if it rerendered.
CodeSandbox: https://codesandbox.io/s/bold-cloud-iv0rv
(If we add a new car still the static component is highlighted in green, but as per Memo, it should not rerender.)
Now my doubt, Is paint flashing does not work correctly or React.memo having issues?
Reason
If you use React.memo, you need to use it from parent down to all the child till the end.
Since React.PureComponent share the same feature with React.memo, you can find those explanation in the document as below.
Furthermore, React.PureComponent’s shouldComponentUpdate() skips prop updates for the whole component subtree. Make sure all the children components are also “pure”.
Result
By changing parent component Cars to memo
// Before
export default Cars;
// After
export default React.memo(Cars);
You could find the react-dev-tools in chrome will only show the parent component re-rendered this time as well as no child console.log fired, which is correct. (Compare to previous, all the child also got highlighted)
Before
After
Conclusion
Both console.log and react-dev-tools worked well, you may just need to implement in a proper way following your demand.
I have a structure where my Parent Component (Layout)(Presentational Component) will render the DOM which has a child component (Fitme) which will get the scroll height and client to check whether the child is overflowing or not, and reduce/increase the font size.
As React doc says, Parent did mount will be called after all child. So my fitme component gets executed before parent do the paint. It makes child to not work as expected.
Requirement: As I want the Child to be plug-able, i dont want to pass any property in props from parent to notify the child about the mount state.
Edited: Please find the sandbox link.
For the First time (after load), it will not be fitting the text. but if you change the dom, so that it will rerender the component. it will work.
https://codesandbox.io/s/k2051pwl83
Update: I guess this is the issue with the plugin i am using to apply css in js way (aphrodite). not sure about it, but i have raise a ticket in git. and code is update in the sandbox.
Thanks in Advance,
First of all, there is nothing related to React. React does its job to the best. I should have not doubted React.
This issue is related to Aphrodite. It uses asap to to schedule buffer flushing.
As doc says in caveats section,
If you measure DOM elements' dimensions in componentDidMount or componentDidUpdate, you can use setTimeout or flushToStyleTag to ensure all styles are injected.
So i have to add setTimeout in componentDidMount or componentDidUpdate.
Here is the updated CodeSandbox : https://codesandbox.io/s/4364jvm7q9
I have a large array that I am using in a component (component A) with *ngFor with a nested *ngFor.
Component B initialises a jquery plugin which registers a document mousemove event handler, I am using this.zone.runOutsideAngular to init the plugin and I am calling this.ref.detectChanges() in the callback as I need to update the UI on mousemove inside the component B.
Component A is not a child of component B.
As soon as the component A is rendered change detection becomes very slow. the array does not change and I am using the ChangeDetectionStrategy.OnPush strategy for component A but when I fire ref.detectChanges() inside component B, ngDoCheck gets called on component A and I can see a noticeable jank on mousemove.
Is there a way to tell angular to completely ignore the large array of items in component A and allow me to handle when the UI should be updated? I thought that using ChangeDetectionStrategy.OnPush would give me what I need but I have tried removing all #Input()s from component A and anytime I call this.ref.detectChanges() inside component B it is still firing ngDoCheck and it is obvious that this is very slow.
I can scroll through the list of items no issue, but it is when I am triggering the detectChanges inside the mousemove on component B that is causing the issue. I know I could manually update the DOM but I think this would just be a workaround as it would only address the jank on mousemove and not the issue around the change detection being slow.
I have got to the bottom of this issue.
The problem was that inside component A for the nested *ngFor I was using a child component to render each sub item which meant that although I was using the ChangeDetectionStrategy.OnPush strategy, it still required a ref check for each item.
I have now moved the html from the child component into component A directly and this has had a huge impact on performance.
this.ref.detach() to remove the detector from from the tree completely, that should stop the checking. Then you can still call detectChanges to do it manually, and reattach to bring it back online.
Maybe also debouncing the mousemoves (rxjs debounceTime()) might help, unless you really need to track every mousemove?
One more optimization if you already didn't, add trackBy: yourTrackByFn to the ngFor(s).
A select element within a React.js component takes 2 clicks to update in Firefox, but updates correctly on first click in Chrome and Safari.
It's a component using Redux + React-Redux and the value of the select element is dictated by a store value passed to it via mapStateToProps.
React Devtools shows the value of the select element updating correctly but the DOM itself doesn't update on the first click.
Redux Devtools is showing the correct actions being passed and correct state changes being made.
I've created a isolated recreation of the component tree in CodeSandbox (it functions correctly here so not a lot of help): https://codesandbox.io/s/jl7rpw3635
Here's a gif of the problem
Thanks in advance!
I ended up solving this issue by modifying the select component to render its options on mount, then attach them to a class property, avoiding re-rendering each option again every time the select component updates (they never change so it probably makes sense to do this) - https://codesandbox.io/s/m7m2qqp9py
Child component property update is triggering all the parent component willUpdate,willRender,didUpdate and didRender life cycle hook methods. but I just updated property which is visible only the child component, It has nothing to do with parent component.
Twiddle to check.
Twiddle to check with powerselect - when mouse over dropdown option it's triggering the all of its parent component willUpdate,willRender,didUpdate and didRender life cycle hook methods.
Is there any way I can avoid this behavior?. [I tried with/without this._super(...arguments)]
PS: The reason is, I am doing some heavy operation in didRender hook of the parent component I don't want to run this unnecessarily. (One solution I can think of is manually checking for the specific property alone is changed or not by myself and run the heavy operation upon the condition).