React: <React.Fragment> vs array - reactjs

I was reading the React doc and get confused by the topic Fragments.
Since we can basically return an array in React, in what situation would one need <Fragements />?
Here is a code sample:
const ReturnArray = () => {
const items = [
<div key={1}>Item 1</div>,
<div key={2}>Item 2</div>,
<div key={3}>Item 3</div>,
]
return items
}
const ReturnFragments = () => {
const items =
<React.Fragment>
<div key={1}>Item 1</div>
<div key={2}>Item 2</div>
<div key={3}>Item 3</div>
</React.Fragment>
return items
}
I think they are the same.
Most existing topics talk about "key warning issues" like this on github, but I just want to know the use cases of <Fragments />
Edit:
Please tell me if there is anything ambiguous.
To be specific:
Please explain the difference between <ReturnArray /> and <ReturnFragments />. They both return multiple elements without useless <div> tag. Why bother using the extra <React.Fragment /> part?

Official document says
Using array notation has has some confusing differences from normal
JSX:
Children in an array must be separated by commas.
Children in an array must have a key to prevent React’s key warning.
Strings must be wrapped in quotes.
So to make it simple, React provides Fragment component that can be used in place of arrays.
Consider how we can wrap multiple children using array
render() {
return [
"Some text.",
<h2 key="heading-1">A heading</h2>,
"More text.",
<h2 key="heading-2">Another heading</h2>,
"Even more text."
];
}
And how it can be achieved using Fragments.
render() {
return (
<Fragment>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</Fragment>
);
}
Taken directly from official document.
Fragments can be written as below aswell.
render() {
return (
<>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</>
);
}

There are two major advantages of using Fragments over array in return statement
Simplistic syntax similar to Other components so that you don't have to worry about return comma separated values, wrapping strings in quotes etc
Fragments can take attribute such as key which is often important when you are returning data from within map. You can't do that using an array.
Example
const ReturnFragments = () => {
const items = list.map((item) => {
<React.Fragment key={item.id}>
<div key={1}>Item 1</div>
<div key={2}>Item 2</div>
<div key={3}>Item 3</div>
</React.Fragment>
})
return items
}

Fragments and arrays are intended to address different use cases and behave differently in one important way.
Fragments will not warn if you omit a key property, while arrays will.
If your component returns several static children, return a fragment.
<Fragment>
<li>One advantage of our product is lorem</li>
<li>Another is ipsum!</li>
</Fragment>
If your component returns dynamically generated children, return an array.
items.map(item => <li key={item}>{item}</li>);
I am paraphrasing the maintainers' responses to an issue I opened in the React repo about a similar question. I highly recommend reading it for more detail: https://github.com/facebook/react/issues/12776

When you create a new component, the render method needs to return one and only one element so usually it's a wrapper of many elements, instead of creating a useless div to the dom, you can use the fragment component.
Quote from React Fragments docs :
A common pattern in React is for a component to return multiple elements. Fragments let you group a list of children without adding extra nodes to the DOM.
Without Fragments
render() {
return {
<div> ---> Useless root
<div>fake</div>
<div>fake</div>
<div>fake</div>
</div>
}
}
With Fragments
render() {
return {
<React.Fragment> ----> Not rendered to the DOM
<div>fake</div>
<div>fake</div>
<div>fake</div>
</React.Fragment>
}
}

Related

In React, can a component return the results of array.map without the empty element wrapper <> </>? [duplicate]

I was reading the React doc and get confused by the topic Fragments.
Since we can basically return an array in React, in what situation would one need <Fragements />?
Here is a code sample:
const ReturnArray = () => {
const items = [
<div key={1}>Item 1</div>,
<div key={2}>Item 2</div>,
<div key={3}>Item 3</div>,
]
return items
}
const ReturnFragments = () => {
const items =
<React.Fragment>
<div key={1}>Item 1</div>
<div key={2}>Item 2</div>
<div key={3}>Item 3</div>
</React.Fragment>
return items
}
I think they are the same.
Most existing topics talk about "key warning issues" like this on github, but I just want to know the use cases of <Fragments />
Edit:
Please tell me if there is anything ambiguous.
To be specific:
Please explain the difference between <ReturnArray /> and <ReturnFragments />. They both return multiple elements without useless <div> tag. Why bother using the extra <React.Fragment /> part?
Official document says
Using array notation has has some confusing differences from normal
JSX:
Children in an array must be separated by commas.
Children in an array must have a key to prevent React’s key warning.
Strings must be wrapped in quotes.
So to make it simple, React provides Fragment component that can be used in place of arrays.
Consider how we can wrap multiple children using array
render() {
return [
"Some text.",
<h2 key="heading-1">A heading</h2>,
"More text.",
<h2 key="heading-2">Another heading</h2>,
"Even more text."
];
}
And how it can be achieved using Fragments.
render() {
return (
<Fragment>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</Fragment>
);
}
Taken directly from official document.
Fragments can be written as below aswell.
render() {
return (
<>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</>
);
}
There are two major advantages of using Fragments over array in return statement
Simplistic syntax similar to Other components so that you don't have to worry about return comma separated values, wrapping strings in quotes etc
Fragments can take attribute such as key which is often important when you are returning data from within map. You can't do that using an array.
Example
const ReturnFragments = () => {
const items = list.map((item) => {
<React.Fragment key={item.id}>
<div key={1}>Item 1</div>
<div key={2}>Item 2</div>
<div key={3}>Item 3</div>
</React.Fragment>
})
return items
}
Fragments and arrays are intended to address different use cases and behave differently in one important way.
Fragments will not warn if you omit a key property, while arrays will.
If your component returns several static children, return a fragment.
<Fragment>
<li>One advantage of our product is lorem</li>
<li>Another is ipsum!</li>
</Fragment>
If your component returns dynamically generated children, return an array.
items.map(item => <li key={item}>{item}</li>);
I am paraphrasing the maintainers' responses to an issue I opened in the React repo about a similar question. I highly recommend reading it for more detail: https://github.com/facebook/react/issues/12776
When you create a new component, the render method needs to return one and only one element so usually it's a wrapper of many elements, instead of creating a useless div to the dom, you can use the fragment component.
Quote from React Fragments docs :
A common pattern in React is for a component to return multiple elements. Fragments let you group a list of children without adding extra nodes to the DOM.
Without Fragments
render() {
return {
<div> ---> Useless root
<div>fake</div>
<div>fake</div>
<div>fake</div>
</div>
}
}
With Fragments
render() {
return {
<React.Fragment> ----> Not rendered to the DOM
<div>fake</div>
<div>fake</div>
<div>fake</div>
</React.Fragment>
}
}

Is it possible to render a react class when given its name as a string?

Here's a smaller example of what I'm trying to do, I don't know if it's possible to do something similar or I should use an entirely different method.
import {Design1, Design2} from './page-designs';
let designs = {
"page1":"Design1",
"page2":"Design2",
"page3":"Design1",
"page4":"Design2"
}
class DesignedPage extends React.Component {
let Design = designs[this.props.page]
render(){
return(
<div className="row flex-fill d-flex">
<div className="col-1"></div>
<Design /* This is the line that fails */
data = {this.props.data}
/>
</div>
</div>
)}
}
class Main extends React.Component {
render(){
return(
<DesignedPage
page = {this.props.openPage} /*this could be any of page1-4 depending on button a click*/
data = {this.props.data}
/>
)}
}
Ideally this would render the react elements Design1 or Design2 based on what props.page is passed, but instead it returns
"Warning: <Design1 /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements." and "The tag <Design1> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter."
I've thought of making a long if, elseif, elseif.. statement in DesignedPage (the actual code has many more than 2 designs), which I'm fairly confident would work, but looks very messy in comparison.
You can't render the component name by getting its name as a string. You need to map the string to the component iteself:
let designs = {
"page1":Design1,
"page2":Design2,
}
If you pass a string, react would think it's a HTML tag, hence it say'Design1' tag is unrecognised. Also, you could import the components and use them as values in the designs object in place of strings.
let designs = {
"page1":Design1,
"page2":Design2,
"page3":Design1,
"page4":Design2
}
make one function that return react component..
getComponent = ({data, pageName}) => {
if(pageName === "page1") return <Desig1 />;
if(pageName === "page2") return <Design2 />;
}
and call function from render of DesignedPage component
const {page, data} = this.props;
return(
<div className="row flex-fill d-flex">
<div className="col-1">
{getComponent(page, data)}
</div>
</div>
)

Can I use React.Fragment for list rendering while have the "key" assigned?

Can I use React.Fragment inside the list rendering and assign a key to this 'Fragment' parent?
I'm trying to build a projects list layout using css grid, which requires all the elements is direct child of the container. Let's say the desired result will be look something like this.
<div className="container">
<img src="imgPathFor1stProject">
<h1>title for 1st project</h1>
<p>description for 1st project</p>
<img src="imgPathFor2ndProject">
<h1>title for 2nd project</h1>
<p>description for 2nd project</p>
...
</div>
But we all know the list render requires to return a single enclosing tag which we can tackle by using React.Fragment. Then the jsx will look like this:
projects.map(project => (
<>
<img src={project.imagePath}/>
<h1>{project.title}</h2>
<p>{project.description}</p>
</>
));
But here comes the problem, I can't assign a key to each child of the list since there's no actual element that wrapping all these elements.
I tried do: < key={project.id}> and <React.Fragment key={project.id}>, both doesn't work.
Is there a way to solve this? I still want to apply display:grid on .container element.
Thanks!
https://reactjs.org/docs/fragments.html
Keyed Fragments
Fragments declared with the explicit syntax may have keys. A use case for this is mapping a collection to an array of fragments — for example, to create a description list:
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
// Without the `key`, React will fire a key warning
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</React.Fragment>
))}
</dl>
);
}

Why return multiple elements in React is not allowed?

it can only return only one element tag in render.
In v16, we can render multiple elements by using an array.
so why cannot straightly write mutiple element tags in React?
render(){
return(
<div />
<div />
)
}
I mean that why cannot render multiple elements but not how to render mutiple elements.
React implementation relies on constructing a tree like structure which it uses to for reconcilation. When you return multiple elements from React elements from the render method, the assumtion that the tree will have one root node for the Component will not longer hold, hence making it difficult to process reconcilation algorithm.
Thus react gives you a limitation to provide a root node. If you return an array of elements from v16 onwards, react will internally create a dummy node as the parent.
From version 16.2 onwards React provides a React.Fragment component, that provides a cleaner syntax to return multiple elements
render(){
return(
<React.Fragment>
<div />
<div />
</React.Fragment>
)
}
React needs a parent element to render anything. You could put them in an array, or use a tool they gave for this exact purpose, the fragment component.
A fragment is just an empty node that won't show in the DOM allowing you to return multiple JSX components next to each other :
render(){
return(
<>
<div />
<div />
</>
)
}
If your linter is not a fan of this, you can use React.Fragment instead :
render(){
return(
<React.Fragment>
<div />
<div />
</React.Fragment>
)
}
The short answer to your question is... well this is just how React works and how their rendering engine is designed.
For now, multiple elements put together will not be interpreted as an array.
You can try
render(){
return[
<div> Div 1</div>,
<div> Div 2</div>,
<div> Div 3</div>,
<div> Div 4</div>
]
}

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!

Resources