How to inject data in a html string in React? - javascript

I have a html string that contains a link. I need to add the attribute rel="noopener" for security purposes. The html string is injected through dangerouslySetInnerHtml:
const Component = ({ message }) => {
return (
<div>
<div dangerouslySetInnerHTML={{ __html: message }} />
<div>
);
};
The string looks like: Hello check out this page
So the desired output would be: Hello check out this page
How to do it?

Try this:
const Component = ({ message }) => {
function secureATags(html) {
// Parse HTML
let doc = (new DOMParser()).parseFromString(html, "text/html")
// Append attribute
doc.querySelectorAll('a').forEach(entry => {
entry.setAttribute('rel', 'noopener')
})
// Reserialize to HTML
return doc.body.innerHTML
}
return (
<div>
<div dangerouslySetInnerHTML={{ __html: secureATags(message) }} />
<div>
)
}

I would use the Browser DOM to achieve this, as follows:
const div = document.createElement("div");
div.innerHTML = 'Hello check out this page';
div.childNodes[1].setAttribute("rel", "noopener");
console.log(div.innerHTML);
If the actual HTML text is more complex than in your example, then div.childNodes[1] will need to be replaced with code that looks for and selects the proper node. But even then (or especially then?), this is probably the easiest and most reliable way to achieve your goal.

Direct use of setDangerousInnerHtml is strictly not recommended due to security issues.
you can use a plugin on npmjs.org pkgname: React-html-parser for injecting the html safely

Maybe consider using a function or component that puts it all together based on the data you send in? E.g.
function App() {
const linkBuilder = (textBefore, linkText, textAfter, href) => {
return (
<div>
{textBefore}
<a href={href} target="_blank">
{linkText}
</a>
{textAfter}
</div>
);
};
return (
<div>
{linkBuilder("Hello check out ", "this page", "", "www.google.com")}
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Also according to this you don't need rel=noopener anymore if you use target="_blank". But if it's necessary you can pass it in as a boolean and apply it on the function/component side.

Related

Edit a html getting from backend and render it in a react website

let htmlFromBackend = `Some backend HTML in table format`
return (
<div dangerouslySetInnerHTML={{ __html: htmlFromBackend}}></div>
)
I want to add some buttons(I want to add a button in every <tr></tr>) and elements in the html getting from backend. How it can be acheived?
For the simplest solution, you can prepend the HTML directly. For example:
return (
<div dangerouslySetInnerHTML={{ __html: `<button onclick="alert('Nice!')">Alert</button>${htmlFromBackend}`}}></div>
)
To prepend buttons on multiple elements of the HTML, you can either use RegExp or parse it first. I'll show you an example that parses the HTML first and edit it.
function preprocessHTML(html){
const element=document.createElement('div');
element.innerHTML=html;
// do any operations you like, for example:
for(const child of element.children){
const btn=document.createElement('button');
btn.innerText='Click Me!';
child.prepend(btn);
}
return element.innerHTML;
}
return (
<div dangerouslySetInnerHTML={{ __html: preprocessHTML(htmlFromBackend)}}></div>
)
If you want to prepend with React, you can use ReactDOM.createPortal to render any JSX element to any HTMLDom.

How to render html element in pre / REACT

Hello I am working on a blog post creation tool and I need a engine when I write in textarea <hr/> I get a line, or when I write <img/> I get an image but it doesn't render.
The post is written in a textarea and should be displayed in a div.
How to do it?
const PostCreate = () => {
const [postValue, changeValue] = useState('')
const handleChangeValue = (e:any) => {
changeValue(e.target.value)
console.log(postValue);
}
return (
<div className='postCreate'>
Create New Post
<textarea onChange={handleChangeValue} value={postValue}/>
<div>
{postValue}
</div>
</div>
)
}
If I write <hr/> I get the string <hr/> instead of a line:
You can use dangerouslySetInnerHTML prop for the div.
<div dangerouslySetInnerHTML={{ __html: postValue }} />
You can check it on this document.

Add <br> tag using replaceAll() in javascript

I have this string:
export default function App() {
const string = 'test data,cars,colors,demo';
return (
<div className="App">
<h1>Hello {string.replaceAll(',','<br>')}</h1>
</div>
);
}
I expect:
test data<br>cars<br>colors<br>demo
But i get one string without breack inside my string. How to achieve what i expect using replaceAll()?
In order to display html from string you have to use dangerouslySetInnerHTML
export default function App() {
const string = 'test data,cars,colors,demo';
return (
<div className="App">
<h1 dangerouslySetInnerHTML={{ __html: `Hello ${string.replaceAll(',','<br>')}`}}></h1>
</div>
);
}
Assuming this is a React application you need to use dangerouslySetInnerHTML to add the new HTML to the page.
function Example() {
const string = 'test data,cars,colors,demo';
const html = `Hello ${string.replaceAll(',', '<br>')}</h1>`;
return (
<div className="App">
<h1 dangerouslySetInnerHTML={{ __html: html }} />
</div>
);
}
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I don't suggest you dangerouslySetInnerHTML. It introduces both security and performance issues, and even if you are working on learning project, you better avoid such an approach.
The reason why your code does not work, JSX does not magically convert string "<br>" into appropriate element (until there is dangerouslySetInnerHTML, sure).
But JSX can render arrays just fine. It allows us to split initial string into elements: string.split(', ') and then inject JSX's <br /> with the help of .flatMap()(think about it as if .join() could return array with additional elements in between elements of source array).
{
string.
split(', ').
flatMap((el, index) => index ? [<br />, el]: el)
}
This approach is way more powerful than dangerouslySetInnerHTML since instead of simple BR you may use any JSX tree with React custom components, context and event handlers.
Another approach is to replace ', ' with newlines and apply CSS style white-space: pre-wrap(check white-space docs on all values available)

Dynamically made strings for id in JSX

I want to make id like this dynamically.
<div id="track_1"></div>
<div id="track_2"></div>
So I gave the id like this from parent component.
export default function Components(props) {
return (
<AudioTrack trackNum="1"/>
<AudioTrack trackNum="2"/>
)
}
then in my AudioTrack Component I got the trackNum and want to use like this
const AudioTrack = (props) => {
return(
<div id="track_{props.trackNum}" ></div>
);
}
Howeber it doesn't work.
Is there any good way?
Since the div prop isn't a constant string, you need {} to indicate an expression, and then either use + to concatenate or a template literal with ${}:
<div id={`track_${props.trackNum}`}></div>

Can you use jsx within template string in React render props?

Is it possible to put JSX inside a template string that is being used as a React render prop?
This is what I'm trying to do, but it leads to the link rendering as [object Object]
const Container = ({ message }) => <div className="from line 4"> {message}</div>;
const Link = () => juan;
const App = () => (
<div>
<Container message={`My message with a ${<Link />}`} />
</div>
);
One thing I tried was to put JSX instead of a template string inside message. This works, but it introduces a new div that isn't needed.
<Container
message={<div>My message {<Link />}</div>}
/>
I made this codesandbox to illustrate the problem
You can use a Fragment to render inline like you are trying to do and to prevent adding a new wrapping <div />:
const App = () => (
<div>
<Container
message={<React.Fragment>My message with a <Link /></React.Fragment>}
/>
</div>
);
Here is a forked version of your Codesandbox using React.Fragment: https://codesandbox.io/s/nrmr9l34vl

Categories