React: Rendering non-existing HTML Element - javascript

While reading the React docs, I realized that in JSX, elements starting with a lowercase letter are treated as HTML element. While putting this into practice, I noticed something interesting: When I pass a non-existing, lowercase element, React renders this just fine.
function Foo() {
return (<foo>hello world</foo>);
}
ReactDOM.render(
<Foo />,
document.getElementById('container')
);
This renders like this:
<div id="container">
<foo data-reactroot="">hello world</foo>
</div>
While trying to find an answer on how this is possible, I encountered several projects dealing with Web Components/Custom Elements in React, which confused me a bit.
Does this have something to do with Custom Elements? Will this work in any browser that is supported by React?

No, this has nothing to do with Custom Elements.
It's perfectly fine to create an element which has a tag name that doesn't represent any predefined HTML element. You will get an instance of HTMLUnknownElement:
const myElement = document.createElement('foo');
console.log(myElement.nodeName);
console.log(myElement instanceof HTMLUnknownElement);
Unknown elements don't have special behavior. If you want create a <foo> element which has special behavior, you have to use Custom Elements.

As PeterMader explains, you are creating a custom html element with that syntax. I'll expand on that answer a bit.
First of all it's important to understand that JSX is just a syntactic sugar and that it ultimately transpiles into JavaScript. React elements are created with createElement.
React.createElement(type, props, children);
What's important to note here is that type can be one of the following:
A reference to a React Component (e.g Foo in your example).
A string.
If you are writing React without JSX, then it doesn't matter if you reference a component as foo or as Foo because you have complete control of how you define your variables. In other words, this is plain JavaScript and JSX rules don't apply.
However, if you are using React with JSX, the following rules apply:
type starts with a capital letter = React Component.
type starts with a lowercase letter = HTML element.
In fact, the official documentation says:
When an element type starts with a lowercase letter, it refers to a built-in component like <div> or <span> and results in a string 'div' or 'span' passed to React.createElement. Types that start with a capital letter like <Foo /> compile to React.createElement(Foo) and correspond to a component defined or imported in your JavaScript file.
We recommend naming components with a capital letter. If you do have a component that starts with a lowercase letter, assign it to a capitalized variable before using it in JSX.
So what about <foo />?
In the event that the type of a React Element doesn't resolve into a React Component and is not a standard HTML, React will still treat it as an HTML element.
To my knowledge, React actually doesn't have any whitelist of "valid" HTML tags, but treats anything starting with lowercase the same way. The result in your browser may differ though, as per PeterMader's response.
As such, this (JSX):
<foo>hello world</foo>
will, in modern browsers, yield (HTML):
<foo>hello world</foo>
which is perfectly fine technically, but probably still not a good idea.

Related

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.

Angular 5 Component Selector in variable returning string instead of component content

I have some data that contains the selector of a component:
{
label: 'This is a label',
componentSelector: '<app-mycomponent></app-mycomponent>'
}
In my app.component.html
Instead of doing this (for example):
<div>
<app-mycomponent></app-mycomponent>
</div>
I would like to do this:
{{data.componentSelector}}
At the moment when I try this it's returning a string instead of replacing it with the contents of the component.
How can I do this?
You cannot interpolate a component, because it is made up of typescript, html, and css. it has to compile to be displayed, if you think about it, it makes sense.
On another note, even if you could interpolate, it would be a poor Angular Pattern, and could have unexpected outcomes especially in production. Stick to the best practices.

React.js: Set innerHTML vs dangerouslySetInnerHTML

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!

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/.

ReactJS component names must begin with capital letters?

I am playing around with the ReactJS framework on JSBin.
I have noticed that if my component name starts with a lowercase letter it does not work.
For instance the following does not render:
var fml = React.createClass({
render: function () {
return <a href='google.com'>Go</a>
}
});
React.render(<fml />, document.body);
But as soon as I replace the fml with Fml it does render.
Is there a reason I cannot begin tags with small letters?
In JSX, lower-case tag names are considered to be HTML tags. However, lower-case tag names with a dot (property accessor) aren't.
See HTML tags vs React Components.
<component /> compiles to React.createElement('component') (html tag)
<Component /> compiles to React.createElement(Component)
<obj.component /> compiles to React.createElement(obj.component)
#Alexandre Kirszenberg gave a very good answer, just wanted to add another detail.
React used to contain a whitelist of well-known element names like div etc, which it used to differentiate between DOM elements and React components.
But because maintaining that list isn't all that fun, and because web components makes it possible to create custom elements, they made it a rule that all React components must start with a upper case letter, or contain a dot.
From the official React reference:
When an element type starts with a lowercase letter, it refers to a built-in component like or and results in a string 'div' or 'span' passed to React.createElement. Types that start with a capital letter like compile to React.createElement(Foo) and correspond to a component defined or imported in your JavaScript file.
Also note that:
We recommend naming components with a capital letter. If you do have a component that starts with a lowercase letter, assign it to a capitalized variable before using it in JSX.
Which means one has to use:
const Foo = foo; before using foo as a Component element in JSX.
The first part of a JSX tag determines the type of the React element, basically there is some convention Capitalized, lowercase, dot-notation.
Capitalized and dot-notation types indicate that the JSX tag is referring to a React component,
so if you use the JSX <Foo /> compile to React.createElement(Foo) OR <foo.bar /> compile to React.createElement(foo.bar) and correspond to a component defined or imported in your JavaScript file.
While the lowercase type indicate to a built-in component like <div> or <span> and results in a string 'div' or 'span' passed to React.createElement('div').
React recommend naming components with a capital letter. If you do have a component that starts with a lowercase letter,
assign it to a capitalized variable before using it in JSX.
In JSX, React Classes are capitalized to make XML compatible, so that it is not mistaken for an HTML tag. If the react classes are not capitalized, it is an HTML tag as pre-defined JSX syntax.
User define components must be Capitalized
When an element type starts with a lowercase letter, it refers to a built-in component like <div> or <span> and results in a string 'div' or 'span' passed to React.createElement. Types that start with a capital letter like <Foo /> compile to React.createElement(Foo) and correspond to a component defined or imported in your JavaScript file.
React recommend naming components with a capital letter. If you do have a component that starts with a lowercase letter, assign it to a capitalized variable before using it in JSX.
For example, this code will not run as expected:
import React from 'react';
// Wrong! This is a component and should have been capitalized:
function hello(props) {
// Correct! This use of <div> is legitimate because div is a valid HTML tag:
return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {
// Wrong! React thinks <hello /> is an HTML tag because it's not capitalized:
return <hello toWhat="World" />;
}
To fix this, we will rename hello to Hello and use <Hello /> when
referring to it:
import React from 'react';
// Correct! This is a component and should be capitalized:
function Hello(props) {
// Correct! This use of <div> is legitimate because div is a valid HTML tag:
return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {
// Correct! React knows <Hello /> is a component because it's capitalized.
return <Hello toWhat="World" />;
}
Here is the reference

Categories

Resources