React dangerouslySetInnerHTML Inside Fragment - javascript

I have a use-case where I need to format some text in React and also render HTML.
Here is an example of what I'm currently trying to achieve:
import React, {Fragment} from "react";
import {renderToString} from "react-dom/server";
function FormatText(props) {
const lines = props.children.split('\n');
return lines.map((line, index) => (
<Fragment key={index}>
{line}{index !== lines.length - 1 && <br/>}
</Fragment>
));
}
const content = {
first: 'This is some text\nwith new\n\nline characters - 1',
second: 'This is some text\nwith new\n\nline <strong>characters - <sup>2</sup></strong>',
};
function App() {
return (
<ol>
<li>
<FormatText>{content.first}</FormatText>
</li>
<li dangerouslySetInnerHTML={{
__html: renderToString(<FormatText>{content.second}</FormatText>)
}}/>
</ol>
)
}
As you can see, I have some content which contains \n characters and HTML. Calling the renderToString function converts the HTML into encoded characters, which means the HTML is not rendered properly.
Is there a way to render HTML inside a react fragment.
Ideally I wanted to do the following (but it doesn't):
function FormatText(props) {
const lines = props.children.split('\n');
return lines.map((line, index) => (
<Fragment key={index} dangerouslySetInnerHTML={{__html: renderToString(
<Fragment>
{line}{index !== lines.length - 1 && <br/>}
</Fragment>
)}}>
</Fragment>
));
}

<Fragment> doesn't adds any node to DOM and so you can't do dangerouslySetInnerHTML on it. It is basically a functionality provided by React to avoid addition of extra node to DOM when you needed to return more than one from return in render. So, if something doesn't exists on real DOM, you can't do anything on it.
renderToString is generally used on node server. When doing server side rendering, you want to send the html from server to client. So, better avoid renderToString also.
The issue is that, html doesn't recognises \n for new line etc. It needs html tags. The approach to use FormatText is fine or you can simply convert the \n to br and then use dangerouslySetInnerHTML inside the <li> tag.
const content = {
first: 'This is some text\nwith new\n\nline characters - 1',
second: 'This is some text\nwith new\n\nline <strong>characters - <sup>2</sup></strong>',
};
function App() {
return (
<ol>
<li dangerouslySetInnerHTML={{
__html: content.first.replace(/\n/g, "<br/>")
}}/>
<li dangerouslySetInnerHTML={{
__html: content.second.replace(/\n/g, "<br/>")
}}/>
</ol>
)
}
ReactDOM.render(<App/>, document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Hope it helps. Revert for any doubts.

Hi I guess it is not possible, only way hot to pass formated html into DOm is via dom element DIV.
Maybe this link could help you or point to
https://reactjs.org/docs/dom-elements.html

Related

rendering an array of strings and add between each element a new line - ReactJS

What I'm trying to do is in the return of a render method, to add a newline between each element of an array (of strings).
I've tried two ways (one of them is commented):
import React, { Component } from 'react'
export default class extends Component {
render() {
return (
<div>
<div>
<div className='Dog'>
{this.props.name}
<p>Age: {this.props.age}</p>
{/* <text>About: {this.props.fact.map((f) => f+'\n')}</text> */}
<p>{this.props.fact.join('\n')}</p>
</div>
</div>
</div>
)
}
}
The result is not what I was looking for:
image #1 - result without any of my attempts (as if - just rendering {this.props.fact}:
image #2 - with my attempt (both attempts end up with same result):
AAAAAAAAAAAH, I'm clueless!
Thanks in advance.
Since whitespace is largely ignored, outputting a newline (\n) won't actually insert a line break. Instead, just use the line break tag (<br>).
In JSX, that looks like <br />. Since you need to return a single element, you'll also want wrap your text and line break in a fragment (<>...</>):
<text>
About:{" "}
{this.props.fact.map((f) => (
<>
{f}
<br />
</>
))}
</text>
If it's just text, you could use dangerouslySetInnerHTML
<p dangerouslySetInnerHTML={{ __html: this.props.fact.join('\n') }} />

Render HTML that is saved in and returned from Backend?

I'm working on a project where I get back a string from the backend, that contains some html. I'd now like to put this html into gatsby:
let htmlFromBackend = `<b>Hello there</b>, partner!`
return (
<div>
{htmlFromBackend}
</div>
)
However, this prints out the html as plain text, so what I see on the screen is literally:
<b> Hello there</b>, partner!
Whereas it should be
Hello there, partner!
How can I render this as proper html?
You can use dangerouslySetInnerHTML however be sure you trust the data coming from the backend as you don't get any protection from XSS attacks.
let htmlFromBackend = `<b>Hello there</b>, partner!`
return (
<div dangerouslySetInnerHTML={{ __html: htmlFromBackend }} />
)
You can use useRef hook and set the element's innerHTML:
const ref = useRef(null);
useEffect(() => {
let htmlFromBackend = `<b>Hello there</b>, partner!`;
ref.current.innerHTML = htmlFromBackend;
}, []);
return <div ref={ref} />;
Install html-react-parser
npm i html-react-parser;
And in the component
import Parser from 'html-react-parser';
let htmlFromBackend = `<b>Hello there</b>, partner!`
return (
<div>
{Parser((htmlFromBackend)}
</div>
)

Multiple tags in list.map() syntax for React

I have the following code:
return (
</React.Fragment>
...
<div className="col-md-6">
{firstHalfy1.map(month => (<Field key={month.id} {...month}/>))}
</div>
</React.Fragment>
);
I want to add another tag/functional component after the component, but the syntax doesn't seem to work. Ideally I want something like:
{firstHalfy1.map(month => (<Field/><Component2/>))}
is this syntax possible as I am trying to render a button (Component2) after every input (Field)?
Thanks!
{firstHalfy1.map(month => (<div key={your key}><Field/><Component2/></div>))}
You need a wrapper for those components, such as a div or React.Fragment. Plus you need a key for each month.
You can use from fragment like this:
<>...
This empty tag is also fragment
{firstHalfy1.map(month => (
<React.Fragment key={month.id}>
<Field {...month}/>
<Component2/>
</React.Fragment>
))}

Parse nested HTML that's within a string in React?

I'm building a type ahead feature in React.
I have wrapper component that has an array of objects, and it renders item; which's a stateless component.
So, suppose I have const name= 'Hasan'. Which gets parsed to >> const parsedName = Ha<span>san</span>; assuming the term to search for is san.
I have tried dangerouslySetInnerHTML={{ __html: parsedName }} attribute on the parent element, but it didn't work.
With plain html this would be: el.innerHTML = parsedName
The goal is to style the span as desired. Any ideas?
class Test extends React.Component {
render() {
const name = 'san';
const parsedName = name.replace(new RegExp('san', 'ig'), '<span>span</span>');
return (
<div dangerouslySetInnerHTML={{__html: parsedName}}/>
);
}
}
ReactDOM.render(
<Test/>,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
</div>
Without code its hard to tell what's wrong. I have created a working snippet here that might help you debug your issue.
Updated the example based on the comment.

How to add a <br> tag in reactjs between two strings?

I am using react. I want to add a line break <br> between strings
'No results' and 'Please try another search term.'.
I have tried 'No results.<br>Please try another search term.'
but it does not work, I need to add the <br> in the html.
Any ideas how to solve it?
render() {
let data = this.props.data;
let isLoading = this.props.isLoading;
let isDataEmpty = Object.entries(data).length === 0;
let movieList = isLoading ? <Loader /> : isDataEmpty ? 'No results. Please try another search term.' :
Object.entries(data).map((movie, index) => <MovieTile key={index} {...movie[1]} />);
return (
<div className='movieList'>{movieList}</div>
);
}
You should use JSX instead of string:
<div>No results.<br />Please try another search term.</div>
Because each jsx should have 1 wrapper I added a <div> wrapper for the string.
Here it is in your code:
render() {
let data = this.props.data;
let isLoading = this.props.isLoading;
let isDataEmpty = Object.entries(data).length === 0;
let movieList = isLoading ? <Loader /> : isDataEmpty ? <div>No results.<br />Please try another search term.</div> :
Object.entries(data).map((movie, index) => <MovieTile key={index} {...movie[1]} />);
return (
<div className='movieList'>{movieList}</div>
);
}
You can use CSS white-space to solve the problem.
React Component
render() {
message = `No results. \n Please try another search term.`;
return (
<div className='new-line'>{message}</div>
);
}
CSS
.new-line {
white-space: pre-line;
}
OUTPUT
No results.
Please try another search term.
break text to line:
render() {
...
<div>
{this.props.data.split('\n').map( (it, i) => <div key={'x'+i}>{it}</div> )}
</div>
...
Some HTML elements such as <img> and <input> use only one tag. Such tags that belong to a single-tag element aren't an opening tag nor a closing tag. Those are self-closing tags.
In JSX, one has to include the slash. So, remove <br> and try <br />
Here is how I got around this. Let message be the prop/variable that has the string containing line breaks to be displayed in HTML as follows:
message = 'No results.<br>Please try another search term.';
<div>
{message}
</div>
To make this work, we need to use \n instead of break tag <br> and set the following css on the wrapper element of this message as follows:
message = 'No results.\nPlease try another search term.';
<div className="msg-wrapper">
{message}
</div>
CSS:
.msg-wrapper {
white-space: pre-wrap;
}
OUTPUT:
No results.
Please try another search term.
If you don't want put the string inside a <div> you could use <> to do it.
Like this:
var text = <>This is a text in the first line;<br />this is a text in a second line</>;
Just split text by /n, I do this in this way:
<div>
{text.split('\n').map((item, i) => <p key={i}>{item}</p>)}
</div>
Try with span
return (
<div className='movieList'><span>{movieList}</span></div>
);
If you are like in my situation and you don't want to add css, you can do that :
render () {
...
return (
...
<Typography component="p">
...
{(contact.lastname)?<div>Hello {contact.firstname} {contact.lastname}</div>:''}
...
</Typography>
...
);
}
using ` worked for me however i am not sure if it is the exact solution to the problem :
import React from 'react';
import ReactDOM from 'react-dom';
let element = (
<div>
<h1> Hello world</h1>
This is just a sentence <br></br>
But This line should not be in the same previous line. <br></br>
The above content proves its working. <br></br>
npm v6.14.6 | react : {React.version}
</div>
);
ReactDOM.render(element,document.getElementById("html-element-id"))
You can add a span tag and add block as a class.
Pomodoro Technique Timer <span className="block">with Bla</span>
The simplest thing which I did is by creating a component.
const EmptySpace = ({ spaceCount = 0 }) => {
return (
<>
{Array.from({ length: spaceCount }, (item, index) => {
return <br key={index} />;
})}
</>
);
};
export default EmptySpace;
<EmptySpace spaceCount={1} />
In your case you could do something like this:
const msg = (
<p>
No results <EmptySpace spaceCount={2} />
Please try another search term.
</p>
);

Categories

Resources