React.js: Set innerHTML vs dangerouslySetInnerHTML - javascript

Is there any "behind the scenes" difference from setting an element's innerHTML vs setting the dangerouslySetInnerHTML property on an element? Assume I'm properly sanitizing things for the sake of simplicity.
Example:
var test = React.createClass({
render: function(){
return (
<div contentEditable='true' dangerouslySetInnerHTML={{ __html: "Hello" }}></div>
);
}
});
vs
var test = React.createClass({
componentDidUpdate: function(prevProp, prevState){
this.refs.test.innerHTML = "Hello";
},
render: function(){
return (
<div contentEditable='true' ref='test'></div>
);
}
});
I'm doing something a bit more complicated than the above example, but the overall idea is the same

Yes there is a difference!
The immediate effect of using innerHTML versus dangerouslySetInnerHTML is identical -- the DOM node will update with the injected HTML.
However, behind the scenes when you use dangerouslySetInnerHTML it lets React know that the HTML inside of that component is not something it cares about.
Because React uses a virtual DOM, when it goes to compare the diff against the actual DOM, it can straight up bypass checking the children of that node because it knows the HTML is coming from another source. So there's performance gains.
More importantly, if you simply use innerHTML, React has no way to know the DOM node has been modified. The next time the render function is called, React will overwrite the content that was manually injected with what it thinks the correct state of that DOM node should be.
Your solution to use componentDidUpdate to always ensure the content is in sync I believe would work but there might be a flash during each render.

You can bind to dom directly
<div dangerouslySetInnerHTML={{__html: '<p>First · Second</p>'}}></div>

According to Dangerously Set innerHTML,
Improper use of the innerHTML can open you up to a cross-site
scripting (XSS)
attack. Sanitizing user input for display is notoriously error-prone,
and failure to properly sanitize is one of the leading causes of web
vulnerabilities on the internet.
Our design philosophy is that it should be "easy" to make things safe,
and developers should explicitly state their intent when performing
“unsafe” operations. The prop name dangerouslySetInnerHTML is
intentionally chosen to be frightening, and the prop value (an object
instead of a string) can be used to indicate sanitized data.
After fully understanding the security ramifications and properly
sanitizing the data, create a new object containing only the key
__html and your sanitized data as the value. Here is an example
using the JSX syntax:
function createMarkup() {
return {
__html: 'First · Second' };
};
<div dangerouslySetInnerHTML={createMarkup()} />
Read more about it using below link:
documentation: React DOM Elements - dangerouslySetInnerHTML.

Based on (dangerouslySetInnerHTML).
It's a prop that does exactly what you want. However they name it to convey that it should be use with caution

Yes there is a difference b/w the two:
dangerouslySetInnerHTML: React diffing algorithm (https://reactjs.org/docs/reconciliation.html) is designed to ignore the HTML nodes modified under this attribute thereby slightly improving the performance.
If we use innerHTML, React has no way to know the DOM is modified. The next time the render  happens, React will overwrite the content that was manually injected with what it thinks the correct state of that DOM node should be.
That's where componentDidUpdate comes to rescue!

Related

React: Declaring a variable inside a functional component VS using the value inside the returned JSX

Is there any kind of performance difference between the following pieces of code? (Note: These are very simple examples):
const FunctionalComponent = props => {
const a = <span>Hello World</span>;
return (
<h1>
{a} {props.counter}
</h1>
);
};
const FunctionalComponent = props => {
return (
<h1>
<span>Hello World</span> {props.counter}
</h1>
);
};
I can't seem to find a straight answer on figuring out if there's any kind of performance difference between declaring a variable like this in a functional component VS directly using the value inside the returned JSX (even if it's just a small performance difference).
Well, the second snippet of code should be the more efficient one to use.
The straight forward answer is that there's a direct correlation between the size of a downloaded script and the performance on the client-side.
Obviously as you said, this is just a simple example and the effect of declaring one extra variable will be negligible. However, as you add more variables there will be a slightly reduced performance since additional memory allocation and fetching happens.
Fetching occurs when you use the variable after you declare it. Somewhere on the low level processing of your script the value of your a const will have to be fetched from memory every time you use it. In your example, instead of just adding <span>Hello World</span> to your dom, you're fetching <span>Hello World</span> from memory (since its stored in a variable). And then it's added to the dom.
Memory allocation simply refers to the act of creating the const and storing its value in memory.
To sum it all up:
Instead of the functional component just returning elements that will be added to the dom, the functional component is storing a constant, fetching the value of the constant and then returning the dom elements. On a larger scale there could be significant performance loss as a result of this.

How to get the component that rendered a dom element with Vue.js

How to get the component that rendered a dom element with Vue.js ?
For example, suppose you want to retrieve which component "owns" a dom element that the user has selected, how would you do ? (it seems to be implemented in the dev tools, but I can't find a way neither in the documentation, neither on SO)
(I mean, given the DOM element, I know how to retrieve what element is selected)
DISCLAIMER : This may not be the right solution for common use cases. Always prefer handling event & co. in the root component using direct sub-component reference if you can
I do not know if this is safe or officially supported, but assuming you're trying to access the component from some external-to-Vue code, this will return the VueComponent object for a given DOM element (substitute your own DOM selector as needed):
document.getElementById('foo').__vue__
If used on the app's root element, it will instead return the Vue constructor object.
(This appears to work in both Vue 1.x and 2.x.)
This is possibly not the most elegant solution, but you can use mixins to achieve this:
var elemOwner = {
mounted: function() {
this.$el.setAttribute("isVueComponent", this.$options.name);
}
};
As long as you set the mixin to the components you need it in, when you click an element you can test the attributes to see if there's a component name in there.
See this codepen for a fuller example: https://codepen.io/huntleth/pen/EpEWjJ
Clicking the smaller blue square will return the component name of the component that rendered it.
EDIT - It should be noted though that this obviously would only work if the element is actually inside that components root element. I think that would be the case for almost all uses.
Getting this.$parent refers to the parent of a component.

Issue Faced while appending the new child DOM into parent in ReactJS

I am trying to append the N number of 'intentSection' by onClick and getting added only string instead of DOM).
It directly appends the following as string instead of DOM.
<div className="intent">..</div>
Here's my code:
let intentSection = '<div className="intent">..</div>';
let reactfindDomNode = ReactDOM.findDOMNode(intentContainer);
$(reactfindDomNode).append(intentSection);
While the answer in the comment would probably work, manipulating the actual DOM is an anti-pattern and usually a bad idea. What you should be doing is utilizing the virtual DOM.
It isn't clear what you are trying to do here, but I guess it can be done by using the state to determine whether a component should be rendered inside the render method return

Render XML data with ReactLayout

The XML data in PhoneCallResponse does not appear in the generated HMTL.
Probably because React uses XML for its components.
Is there a special way to handle this?
PhoneCallResponse = React.createClass({
render() {
return (
<Response>
<Say voice="alice">
Thanks for the call. Configure your number's voice U R L to change this message.
</Say>
<Pause length="1"/>
<Say voice="alice">
Let us know if we can help you in any way during your development.
</Say>
</Response>
);
}
});
FlowRouter.route('/twilio/', {
action: function() {
ReactLayout.render(Layout, {
content: <PhoneCallResponse />
});
}
});
React reasons with JSX (it's not XML!) this way: Capital letter at the start of the tag indicates a React component. Lowercase tags are rendered straight out as DOM elements without further computation. At the end of the day, React renders HTML views, not XML.
Your code will cause a reference error, as neither Response, Say or Pause are defined components. The reason is the way JSX is transpiled before React ever does anything: JSX tags with a capital letter are treated as components. So <Response></Response> will be converted to React.createElement(Response, {}, null), and if Response isn't a valid React component within the scope, the javascript engine will complain, and everything crashes. Lowercase tags however are treated as strings, and React will simply render an HTML tag <response></response>
If you insist on mixing XML in with your HTML, or your goal is to render an XML document, then use lowercase tags or dangerouslySetInnerHTML which is the React way of injecting strings in the DOM. Keep in mind however that HTML5 is not XML in the way that XHTML was, and XHTML is more or less dead. Standard compliant custom tags these days follow the pattern <x-custom-tag>, <x-another-custom-tag> and so forth, just to make sure you avoid any name collision that might be caused by new tags in a future HTML standard.
On a side note: React plays well with HTML5 custom elements and shadow DOM. Sadly, this part of the HTML standard is very "work in progress", and apparently miles away from being ready to use, but as of Chrome version 49 this example should demonstrate the general idea: https://jsfiddle.net/dannyjolie/je8pmazk/.

Removing Chrome's "translate" DOM Property

I'm working with some legacy code where the original developers made heavy use of generating HTML DOM nodes with a non-standard attribute named translate
<span translate="[{"shown":"My Account","translated":"My Account","original":"My Account","location":"Text","scope":"Mage_Customer"}]">My Account</span>
and then traversing/searching for those nodes with javascript code like the following.
if (!$(target).match('*[translate]')) {
target = target.up('*[translate]');
}
The problem I'm trying to solve is, it appears that Google Chrome automatically adds a translate attribute to every DOM node in the document, and that this DOM node's value is a boolean true. You can see this by running the following Javascript from Chrome's javascript console
> document.getElementsByTagName('p')[0].translate
true
>
Is there anyway to tell Chrome not to populate these attributes? Their presence is wrying havoc with the legacy code. PrototypeJS's match and up nodes treat these boolean object attributes as matches, while the code I'm dealing with is specifically looking for DOM nodes with an attribute named translate. I'd like to find a solution for my problem that doesn't involved rewriting the old Javascript to use methods like hasAttribute.
I tried (as a wild guess) adding the meta attributes mentioned in this article,
<meta name=”google” value=”notranslate”>
<meta name=”google” content=”notranslate”>
but the nodes in the page still has a boolean true translate attribute.
(if it matters, this is Magento's inline translation system I'm talking about here)
The best I've been able to come up with so far is going through every DOM element in the page defining a getter that checks for the existence of an attribute. (the Object.__defineGetter__ guard clause ensures no errors in browsers that don't support modern Javascript)
if(Object.__defineGetter__)
{
var hasTranslateAttribute = function(){
return $(this).hasAttribute("translate");
};
document.observe("dom:loaded", function() {
$$('*').each(function(theElement){
theElement.__defineGetter__("translate", hasTranslateAttribute);
});
});
}
I tried defining a getting on Object.prototype and Element.prototype, but it seems like the browser's native translate is defined higher up the chain, so you need to redefine things on a per element basis.
Replace the nonstandard attribute translate by an attribute like data-translate, which is virtually guaranteed to be and to remain undefined in HTML specifications and in browsers. The data-* attributes were invented to prevent issues like this, and they can also be used to fix them.

Categories

Resources