Ternary statement causing react components to completely disappear briefly - javascript

Here is an example to reproduce: https://codesandbox.io/s/crazy-kirch-f7fso8?file=/src/App.js
To reproduce:
right click and inspect the elements.
In your inspector (assuming you have this capability), right click on the div with the wrapper id and break on subtree modifications.
Click toggle button
Click "resume script execution" arrow to jump through each subtree modification.
Notice how initially, neither imported component renders, then they pop back in on a subsequent render.
Walking through the example in a bit more detail:
I am conditionally rendering 3 types of thing depending on a single variable using the ternary operator.
String - this seems to update immediately
Element - this seems to update just after the string does
Imported component - both components disappear, then one comes back (after element and string).
Does anyone know what may be causing imported components to briefly disappear? This is causing a flash of content that I'd like to avoid.

That is because A and B are different components, so on each toggle, you need to unmount the rendered one and mount the other. This means removing the node from the DOM and then inserting the other one.
The other cases are treated as the same element with just a text content change. Not mounting/unmounting happening here.
About the flashing, i cannot reproduce on FF or Chrome. Only if i have the break-on-modifications enabled i see it.

Related

Remove React Component contentEditable Warning

I've created a react component which is a form within a model in a pop-up hover. But Every time I load the page the get the following Warning in the console, and I want to remove it.
Here's the warning-
Warning: A component is `contentEditable` and contains `children` managed by React.
It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated.
This is probably not intentional.
div
FormControl#http://localhost:3001/static/js/1.chunk.js:31598:18
WithStyles#http://localhost:3001/static/js/1.chunk.js:78328:25
TextField#http://localhost:3001/static/js/1.chunk.js:52126:22
WithStyles#http://localhost:3001/static/js/1.chunk.js:78328:25
Please let me know if I've missed out on any information or code snippet.

Host binding throws ExpressionChanged error even when calling detectChanges

See the following Stackblitz: https://stackblitz.com/edit/angular-psqzbo?file=src%2Fapp%2Fhello.component.ts
Notice that there are two bindings to the width member: one is in the template, and the other is a host binding. The host binding is commented out right now. Notice that no ExpressionChanged error is being thrown-- this is because this.cdr.detectChanges() is being called after we update width in ngAfterViewInit.
Now uncomment the host binding. Observe that an ExpressionChanged error is thrown. Why? What makes these bindings different? Is this a bug?
EDIT: This is not a dupe of the linked question. I know why detectChanges is needed here, my question is why it is not working on the host binding. Please re-read.
This error occurs for you because you are making a change that invalidates the previously rendered component view.
Quote from this documentation
Angular's unidirectional data flow rule forbids updates to the view after it has been composed. Both of these hooks fire after the component's view has been composed.
What this means for your situation:
When Angular first renders/composes the view of this component (prior ngAfterViewInit) the width of the element is set to an initial value. This code changes the width of an element, which causes a change to the view.
I think this example using background color makes this more obvious. In the first pass rendering the view, the color is red (and you can briefly see this on the screen). In your situation, the width binding is undefined on the first pass through.
Then, the ngAfterViewInit causes a change that makes the previously created view invalid, changing the width or the color, triggering the error. I translate this error as Angular saying "I did a bunch of work and made a view that was perfect, then you did something that made that work worthless. You shouldn't do that, because it interrupts some performance optimizations/assumptions I have".
This can be fixed by ensuring the component change happens after the ngAfterViewInit method has finished running, by using setTimeout. Fixed example Or by ensuring the component change happens before the view is rendered by moving it to ngOnInit.
You may notice that I do not have detectChanges in the examples I created. This is intentional, as that is a red herring and actually has no relation to the problem (though you correctly state this is necessary for the template binding to work). When the host binding is commented out in your example, there is no expression problem because this.width has no impact on the rendered view of the component.
There is no issue when the binding is in the template because this is causing changes to the content of the component - not the component's own view.
Speculation / Things I don't fully understand:
I believe this behavior boils down to changes to the Shadow DOM vs Light DOM (I'm making guesses based on info from this SO question). At the time ngAfterViewInit is running, I believe there is already an empty tag <my-component></my-component> in the Light DOM (IE actually on the page). Changing the child content does not cause an issue, because at this point it doesn't actually exist on the page and is part of the Shadow DOM. However, changing a host binding triggers a change to the real elements on the page (part of the Light DOM) - hence the error.

React.js call components into setState

I'm trying to make a toaster component.
https://codesandbox.io/s/62n81oy4pr
in index.js, this.setState({ toasterText: "Room added to summary." });
from this code, you can change the value of the toaster whatever you want and When you click the button several times, the toaster keeps showing and then disappear after 2.6 sec from the last click.
However, When I set a component (or even a div) into the 'toasterText' and click the button,
something like this,
this.setState({ toasterText: <Component>asdfasdf</Component> });
the toaster appear by one click and disappear as soon as I click the button again.
I think I can do something with the componentDidUpdate or am I just not allowed to set components in setState??
sorry for my bad English.
The reason it "disappears" is because the component is re-mounted (destroyed and re-created), as opposed to re-rendered (text updated) in your first example.
While it is technically possible to do this, it is considered anti-pattern for numerous reasons (the problem you experienced being one of them). I can't think of any reason you'd ever want to take that approach; just stick with updating the text.
If you really have to implement the second approach, try adding a key prop to the component. That should prevent the re-mounting behavior.

Why does the value attribute from an input[text] is different from what the browser renders?

I'm getting this strange behaviour in a very specific set of inputs on one my applications. I create some inputs and I can see them as I created them on the Elements panel (google chrome), but the way the browser renders it is different.
Note how the input is renders with comma instead of a point, but the value attribute uses a point
When I get a referente to that element using the selector API, I get this:
A direct reference to the Dom Element will return 11,00. The tag has 11.00 and jQuery returns the 11,00. I've removed all js that interacts with this element (masks, events, etc) and the issue still happens.
I've been swearing at the DOM for a day and a half, but I know this is most probably an issue with my application. What bothers me the most is that the browser does not honor what I see in the elements panel.
This is the small piece of code that creates the element, stopped right before the tag is created. Note the variables values in the right panel:
Could someone give me a hint about what could be causing this difference in between element, view and attributes? If possible, I'd like to know what/how this is happening in depth.
Thank you in advance

IE8/9 dynamically inserted elements are invisible for a nondeterministic amount of time

Has anybody else encountered this problem and arrived at a satisfactory solution?
Basically I have a very complex web application which under IE9 Quirks mode (There is no possibility of switching the doctype as of right now) a specific type of element being dynamically inserted via jQuery is failing to present itself fully. It participates in page layout, but the element itself does not appear.
Sometimes 5 seconds later, and sometimes 30 seconds later, and sometimes even later than that, the element will finally magically appear.
I have used the F12 dev tools (both with and without the "Begin Debugging" clicked) and it does present the element in question, and it does not show the blue border indicator (as it's apparently not being rendered no rendering of its border appears, this is logical).
The temporary functioning workaround has been to dynamically remove the element, and reinsert it, in a nested setTimeout() chain. Then I have to re-install click event handlers.
Like this:
if (isQuirksMode()) {
setTimeout(function() {
var parent = jqelement.parent();
var removed = jqelement.remove();
setTimeout(function() {
parent.append(removed);
removed.children('input').click(clickcallback);
},0);
},0);
}
jqpage.append(jqelement);
This code will force the element to be inserted, removed from DOM, and inserted again, and after that it seems to correctly display.
The reason for doing it this way is that I tried adding and removing a class, in hopes it would trigger a re-render, but that did not change any behavior.
My question is, does this behavior ring any bells for anyone, and have you been able to come up with a less expensive way of getting it to behave?
Injecting elements as strings all in one go is much faster than injecting them individually into the DOM.
Build it up in a string - use innerHTML to insert.

Categories