Why is ReactJS rendered HTML filled with empty spans? - reactjs

If I have a render method in ReactJS component like this:
render: function() {
return <div>
<span>some text here</span>
</div>;
}
It ends up rendering some extra spans inside. How can I get rid of these?

It seems that any space between some block is causing this problem. For example:
<div> {foo} </div>
Will be rendered into:
<div><span/>{foo}<span/></div>
It didn't help to use parenthesis to wrap them, maybe because somewhere in my call from a parent component I am not using them.

The solution is to wrap your return in parens:
render: function() {
return (
<div>
<span>some text here</span>
</div>
);
}
Not only is it more readable but it also indicates to ReactJS to ignore the whitespace which allows you to format things as you wish.

Related

Adding script within jsx

In my jsx file I have the following function:
function GetComments({commentsArray}) {
return commentsArray.map((comment) => {
let localId = comment.LocalId;
return (
<div className="comments-container">
<div className="commenter">
<span className="id-label">{comment.LocalId}</span>
<script>
if (comment.ParentCommentId !== null) {
document.write("<span class=reply title=in reply to " + comment.LocalId + "></span>")
}
</script>
</div>
</div>
);
});
}
Now I don't get any errors but when that script tag part is in there, the page just doesn't load, it's just constantly stuck trying to load. Where have I gone wrong with adding a script to this? I am only wanting that element created when that condition is met. I have been looking online and I see that it's different in jsx for adding scripts but confused on how to correctly implement my script
There are a few problems with your code, the biggest of which is the use of document.write, which you should never be using in react code. To understand why, it's important to realize that one of the most fundamental design goals of react is to abstract any direct manipulation of the DOM away from the programmer and instead allow them to specify the way in which a lighter weight virtual DOM reacts to state changes. Try something like the following:
export function GetComments({ commentsArray }) {
return (
<>
{commentsArray.map((comment) => {
const localId = comment.LocalId;
return (
<div key={localId} className="comments-container">
<div className="commenter">
<span className="id-label">{localId}</span>
{comment.ParentCommentId && (
<span className="reply" title={`in reply to ${localId}`} />
)}
</div>
</div>
);
})}
</>
);
}
In addition to doing away with document.write, this addresses the following issues:
Consistent use of localId.
Conditional rendering to render the "reply" span when comment.ParentCommentId is truthy.
Template literals to more clearly express the "in reply to" string. This is maybe a bit more of a style issue than a functional one, but template literals are the modern way to format variables into string templates.
The use of key inside the map to "help React identify which items have changed, are added, or are removed."
Wrapping the whole map expression in a fragment so that it has a return type which is not an array, which is a requirement for it being used as a JSX element.
I have managed to actually come up with a way to solve the issue, don't know if this is the best way.
I created an additional function where I pass the id into it and do the check there.
function ParentIdCheck({id}) {
if (id !== null) {
return(
<span className="reply" title={"in reply to " + id}></span>
);
}
}
and now the original function looks like:
function GetComments({commentsArray}) {
return commentsArray.map((comment) => {
let localId = comment.LocalId;
return (
<div className="comments-container">
<div className="commenter">
<span className="id-label">{comment.LocalId}</span>
<ParentIdCheck id = {comment.ParentCommentId}/>
</div>
</div>
);
});
}
I don't know if this is the best way though, so open to suggestions on how to improve this

Why are Fragments in React 16 better than container divs?

In React 16.2, improved support for Fragments has been added. More information can be found on React's blog post here.
We are all familiar with the following code:
render() {
return (
// Extraneous div element :(
<div>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</div>
);
}
Yes, we need a container div, but it's not that big of a deal.
In React 16.2, we can do this to avoid the surrounding container div:
render() {
return (
<Fragment>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</Fragment>
);
}
In either case, we still need need a container element surround the inner elements.
My question is, why is using a Fragment preferable? Does it help with performance? If so, why? Would love some insight.
It’s a tiny bit faster and has less memory usage (no need to create an extra DOM node). This only has a real benefit on very large and/or deep trees, but application performance often suffers from death by a thousand cuts. This is one cut less.
Some CSS mechanisms like Flexbox and CSS Grid have a special parent-child relationship, and adding divs in the middle makes it hard to keep the desired layout while extracting logical components.
The DOM inspector is less cluttered. :-)
You can find the descriptions of some other use cases in this React issue: Add fragment API to allow returning multiple components from render
Adding to all answers above there is one more advantage: code readability, Fragment component supports a syntactic sugar form, <>. Thus the code in your question can be written more easily as:
render() {
return (
<>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</>
);
}
According to docs,
In React, this desugars to a <React.Fragment/> element, as in the example from the previous section. (Non-React frameworks that use JSX may compile to something different.)
Clutter-free, right ?
Note that you still need to use <Fragment> syntax if you need to provide key to the fragment.
Added features not possible before with JSX
Better semantic jsx markup. Wrapper elements are used when needed not because they are forced to.
Less overall dom markup (increased render performance and less memory overhead)
It as simple as when you don't need a wrapper element you aren't forced to use one. Having less elements is great but I think the biggest benefit is being able to render elements in jsx that weren't previously possible and adding better semantic meaning to wrapper elements because they are optional now.
This wasn't possible before:
<select>
{this.renderOptions()}
</select>
Glancing at the following in React 15 you can't tell if the wrapper element is needed or not:
<span>
<h1>Hello</h1>
{this.getContent()}
</span>
As per the reactjs.org docs most important needs of <Fragment> </Fragment> instead of div's are to avoid breaking HTML semantics. When we use div's instead of <Fragment> </Fragment> we break the HTML semantics.
To know more about html semantics. please click
and also there are cases where if you use div's instead of Fragments it will be invalid html, for example look at this code:
class Columns extends React.Component {
render() {
return (
<div>
<td>Hello</td>
<td>World</td>
</div>
);
}
}
<table>
<tr>
<div>
<td>Hello</td>
<td>World</td>
</div>
</tr>
</table>
Fragments solve this problem.
When working with React, there are cases where you will need to render multiple elements or return a group of related items. Here’s an example:
function App() {
return (
<h1>Hello React!</h1>
<h1>Hello React Again!</h1>
);
}
If you try to run your app with the code above, you will run into an error stating that Adjacent JSX elements must be wrapped in an enclosing tag. This implies that you need to wrap both elements within a parent div.
function App() {
return (
<div>
<h1>Hello React!</h1>
<h1>Hello React Again!</h1>
</div>
);
}
Doing this will fix the error, but it comes with a degree of risk. You are adding an extra node to the DOM, which is not necessary. In a case like this, where the above is a child component that will be enclosed within a parent component, this becomes a problem.
function Table() {
return (
<table>
<td>This is a Table Component</td>
<Columns />
</table>
);
}
function Columns() {
return (
<div>
<td>Hello React!</td>
<td>Hello React Again!</td>
</div>
);
}
The resulting HTML for the Table component will be invalid because of the additional div that was added.
function Table() {
return (
<table>
<td>This is a Table Component</td>
<div>
<td>Hello React!</td>
<td>Hello React Again!</td>
</div>
</table>
);
}
Let’s take a look at a better way of solving this by using React Fragment, which will not add any additional node to the DOM. The syntax looks like this:
function Columns() {
return (
<React.Fragment>
<td>Hello React!</td>
<td>Hello React Again!</td>
</React.Fragment>
);
}
You can also use the short syntax <></> for declaring a Fragment.
function Columns() {
return (
<>
<td>Hello React!</td>
<td>Hello React Again!</td>
</>
);
}
Using <React.Fragment>...</React.Fragment>, we can add a parent tag to our JSX elements without adding an extra node to the DOM.
you can replace the extra div tags with React.Fragment
writing React.Fragment every time is too long for you. React.Fragment has a shorthand syntax that you can use. It is <>...</>.
When you want to group components but don't want a div tag HTML in the generated HTML, you can use fragment. The generated HTML for <> <p> Hello </p> </> is just this: <p> Hello </p>
If we'd have used div container, the <div>…</div> will be generated too!

How can I render a <template /> tag with react?

Sometimes you might need to render web-components from your react app.
Web-components often use a special <template> ... </template> tag.
But if I try to render such markup with react like this:
render() {
return (
<template>
<div>some content</div>
</template>
)
}
then my web-components don't work correctly.
The reason is that JSX does a different job than what the <template /> tags exists for. The idea of a template tag is to not render its children and pretty much handle it like unparsed text (the browser actually parses it just to make sure its valid html, but does nothing more)
But when you write this in JSX:
return (
<template>
<div>some content</div>
</template>
)
you're basically instructing react to create a 'template' element and then create a 'div' element and then to append this div to the template as a child.
So under hood this happens:
const template = document.createElement('template')
const div = document.createElement('div')
const text = document.createTextNode('some text')
div.appendChild(text)
template.appendChild(div)
But what you want is to set the contents of the <template /> as a string. You can use innerHTML for that.
Solution
One solution would be:
render() {
return (
<template
dangerouslySetInnerHTML={{
__html: '<div>some content</div>'
}}
/>
)
}
Now you're asking react to create all those children tags as node elements but letting the browser decide what to do with them.
Nicer solution
You might not want to use dangerouslySetInnerHTML all the time. So let's create a helper component:
function Template({ children, ...attrs }) {
return (
<template
{...attrs}
dangerouslySetInnerHTML={{ __html: children }}
/>
);
}
Now any time you need to use a template you can use it like this:
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
Don't forget to put the inner content in quotes, because it should be a string.
I know that this question has already an answer, but there is another, I guess simpler solution to do that creating hoc (Higher Order Component).
Just create new "component" like this:
// hoc/Template.js
const template = props => props.children
export default template
and then you can use it in your project this way:
import './hoc/Template.js'
...
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
Newer version of react has already build it such a component, so you can achieve same thing without creating component.
import { Fragment } from 'react'
...
render() {
return (
<Fragment>
{'<div>some content</div>'}
</Fragment>
)
}
This is actually a bug in React! https://github.com/facebook/react/issues/19932
When the native browser parses the <template> tag, it appends all children to the template's content fragment, instead of adding them as children. So a template should never actually have any children.
However, if you programmatically build the DOM (the way react-dom does), then the template will have an empty content fragment, since all children are added as children. This is why web components will not behave properly with these templates.
Here's an example of the problem too: https://codesandbox.io/s/new-sun-8l62o?file=/src/App.tsx:1056-1118
The easiest workaround is to use dangerouslySetInnerHTML, like so:
<template dangerouslySetInnerHTML={{ __html: `
<p> Some text </p>
` }} />
This is limited by the fact that you can only supply raw HTML (no custom React elements). However, since you're using the <template> tag in the first place, it seems highly unlikely that you'd also be using React within the template.

Why does putting {''} crash React's rendering?

I recently tried making a component where I had an empty string inside of the curly brackets in the render function. When I tried rendering it, nothing showed up. Anyone know the reason?
Here's an example. Remove line 22 to see the before and after.
http://jsfiddle.net/tb5p9gpk/113/
Cause of the problem: {''}
Because your {' '} is outside the containing <div></div>
render: function() {
return (
<div> /* containing div */
<HashTagInput onUpdate={this.onUpdate} hashtag={this.state.hashtag} />
<HashTagCount hashtag={this.state.hashtag} />
</div>
{' '} /* outside the containing div */
)
}
form the react docs When called, it should examine this.props and this.state and return a single child element.

this.props.children selecting innerHTML

Lets say our component structure is as follows:
CommentList
Comment
CommentList is defined as:
var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
<Comment author="Pete Hunt">This is one comment</Comment>
</div>
);
}
});
and Comment defined as
var Comment = React.createClass({
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
{this.props.children}
</div>
);
}
});
It seems a bit strange that this.props.children is selecting the text "This is one comment". Why is it called "children", shouldn't it be "innerHTML" or something? I feel like I'm missing something.
What you wrap between a component, is not its inner HTML, but is wrapped into a ReactElement, and passed as a prop in props.children.
When you use JSX syntax, whatever is inserted inside the tags of an element, becomes the element 0 of the props.children array of the component. When there is just one child, no array is created and props.children points to the only element. From the docs:
However, when there is only a single child, this.props.children will
be the single child component itself without the array wrapper. This
saves an array allocation.
With react you deal with a virtual DOM that's why you're not interacting directly with the DOM but with the virtual DOM.
this.props.children designates the children being passed onto you by the owner.
https://facebook.github.io/react/tips/children-undefined.html
So here the child will get "This is one comment" probably inside a span (automatically added). Because it's passed by the owner component.
This way you interact with the virtual DOM and change the content of your component.
Hope it helps

Resources