React - Insert an image between the 2nd and 3rd <p> tag - reactjs

I have a React component
<Text>
<div dangerouslySetInnerHTML={{ __html: apiContent}} />
</Text>
that displays HTML coming from an API that may look like the following:
<div>
<p>text</p>
<p>more text</p>
<p>more text</p>
<p>more text</p>
<p>still more text</p>
</div>
How do I insert an image, which is another React component, between the 2nd and 3rd p tag?
I know how to do it in vanilla JS, but have trouble doing it the React way.

I'd be curious to see the other answers, but I feel like inserting a React component inside some raw HTML is pretty much impossible. The options I can think of are:
use some library to turn the raw HTML into a tree of React components, and hopefully this library will also include a way to inject custom components in specific places (I don't know any such library, but there probably is one)
edit the raw HTML to insert an empty container with a unique id at the place you need it (for instance by parsing the HTML into a DocumentFragment, using vanilla JS to insert a div, turning it back into a string, and setting it as innerHTML as you already do), and then use a React portal to inject a React component into that container. Pretty messy...

If it is a react component then it is not html so not logical to put it inside dangerouslySetInnerHTML tag.
you need to rewrite this react component to an HTML string and work with the html that is coming from API as a string.
that means you can split the incoming string of html on the second closing p tag
and add the new html string in the middle.

Related

Apply html tags in a paragraph tag react

I'm getting a text attribute from the API call such as follows
Code
<p>{this.state.eventInformation.summary}</p>
API response for this.state.eventInformation.summary
"\"Be the Petals\", The charity peloton organized by the Club of guarantees to provide non-stop fun for those who are ready to cycle around Colombia! \n Terms and Conditions \n https://docs.google.com/document/d/1444444FXq9YWe71ImTn0vr-aOhrRgY/edit?usp=sharing"
I have added this to a <p> tag in my page. i want to show this as follows --->
""Be the Petals",The charity peloton organized by the Club of guarantees to provide non-stop fun for those who are ready to cycle around Colombia!
Terms and Conditions
https://docs.google.com/document/d/1444444FXq9YWe71ImTn0vr-aOhrRgY/edit?usp=sharing
how can I achieve this
You have different options:
one of them is using dangerouslySetInnerHTML: to do so you need to add this line
<p dangerouslySetInnerHTML={{ __html: this.state.eventInformation.summary}} />
But with this solution, your link won't get clickable.
If you want to make the link clickable as well you should preprocess your text first, you can follow instructions from here
https://stackoverflow.com/a/2536034/12737367
Firstly, as far as I know <p> doesn't support newLine characters and so on. Try to use <pre>. For a link to be clickable you need to put it inside <a> element. To do this I recommend creating a regex to get it into variable and then put it as href parameter of <a> element.
You should use dangerouslySetInnerHTML and replace the built-in method. Here is the code I made on Codesandbox for you. Just make it clean code according to your project and use it.
import react,{useEffect, useState} from 'react';
export default function App() {
const [text,setText]=useState("");
const st ='"Be the Petals", The charity peloton organized by the Club of guarantees to provide non-stop fun for those who are ready to cycle around Colombia! \n Terms and Conditions \n https://docs.google.com/document/d/1444444FXq9YWe71ImTn0vr-aOhrRgY/edit?usp=sharing';
function replaceURL(val) {
var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/ig;
return setText(val.replace(exp,"<a href='$1'>$1</a>"));
}
useEffect(()=>{
replaceURL(st)
})
return (
<div className="App">
<p dangerouslySetInnerHTML={{ __html: text}} />
</div>
);
}

Display HTML on React

I have a React rich text editor which saves long description state as HTML text. I am trying to display the long description on product pages. How do I parse it for it is displaying the HTML text rather than converting it.
It's displaying like this rather than parsing and converting the HTML
<p>hahahah baba is yellow <strong>kkkk very yellow</strong></p><p><strong>ichoo </strong></p>
You can assign this html to a variable and print by using the dangerouslySetInnerHTML prop.
eg
const html = '<p>hahahah baba is yellow <strong>kkkk very yellow</strong></p><p><strong>ichoo </strong></p>';
<div dangerouslySetInnerHTML={{__html:html}}></div>
dangerouslySetInnerHTML is React’s replacement for using innerHTML in the browser DOM. In general, setting HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) attack. So, you can set HTML directly from React, but you have to type out dangerouslySetInnerHTML and pass an object with a __html key, to remind yourself that it’s dangerous.
function createMarkup() {
return {__html: '<p>content...</p>'};
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
Example snippet:
https://codesandbox.io/s/stoic-matan-kzzydv?file=/src/App.js
You can use dangerouslySetInnerHTML as mentioned in React docs
I use react-render-html in my projects.
A sample code will look like this
import renderHTML from 'react-render-html';
renderHTML("<a class='github' href='https://github.com'><b>GitHub</b></a>")

Dangerously Set innerHTML React

I have React frontend and strapi backend.
When inserting data into my strapi backend, the resulting output in my frontend contains html elements.
How can I show the output without the HTML elements? I have the following Gatsby code block,
import ReactMarkdown from "react-markdown"
<ReactMarkdown children={info_} />
The data within {info_} is outputted with the HTML elements, how can I use Dangerously Set innerHTML in my code or is there some other way to achieve this?
If you display an html node within the dangerouslySetInnerHTML property, you put your application at risk for XSS attacks. Long story short, the html could contain malicious code that would harm the user. If you do it, you need to sanitize the content before displaying it. The best option would be to use a battle-tested library such as sanitize-html-react.
You can use DOMParser to create a document from your HTML input and then extract the text like this:
new DOMParser().parseFromString(info_, 'text/html').body.textContent;
Here's an example using a functional form:
I tried putting this into a snippet demo, but the Stack Overflow snippet environment doesn't like something about the syntax. 🤷 ☹️ You can copy and paste it in your JS console to try it.
Note that the embedded script never runs, but its source text is included in the output. If you want just part of the created document's text, you can use a method like Document.querySelector on the created document rather than its body.
function getTextFromHtml (html) {
const doc = new DOMParser().parseFromString(html, 'text/html');
return doc.body.textContent ?? '';
}
// Use:
// assuming `info_` is a string of valid HTML like this:
const info_ = `
<div>
<p>Some text</p>
<p>Some more text</p>
<script>console.log('This script executed!')</script>
</div>
`;
const textContent = getTextFromHtml(info_);
console.log(textContent);
Afterward, you'll have plain text, so you won't need dangerouslySetInnerHTML.

How to test if iframe exists with React testing library?

In my React app I have a component which embeds an Instagram post.
It renders the following HTML structure:
<div data-testid="instagram"><iframe class="instagram-media instagram-media-rendered" id="instagram-embed-3" //etc....
I am using React testing library.
How do I write a test that checks if the iframe element exists and e.g with the CSS class instagram-media?
For accessibility purposes, it is recommended to always use the title attribute on an <iframe> to label/describe its content.
<div data-testid="instagram">
<iframe title="Instagram embed" className="instagram-media ..." id="instagram-
embed-3"></iframe>
</div>
You can then test if the <iframe> element exists with the getByTitle query.
expect(screen.getByTitle('Instagram embed')).toBeInTheDocument();

React : best way to inject Component in dynamically loaded HTML?

I'm new on React (I more at ease w/ jQuery or AngularJS). I have a special case and I don't find a good way to resolve it...
My app contains an area which is like a "document viewer". It loads an HTML content from the backend (via API, using Fetch) and inject it in the "viewer" component. The HTML content loaded looks like an "university report" (it's just a formatted text, only <span> and <p> with class="..." attributes, nothing more).
Ex : <p>Lorem ispum <span>some text</span> loreb bis <span>ipsum</span></p> ...
I load the content, and inject it this way in the render() of my component <Viewer> :
<div dangerouslySetInnerHTML={ getFreshlyLoadedHTML() } />
Easy, it works just fine !
But... Now, I want to inject some "interactive" components in the loaded HTML. For example, some button to give a feedback etc. The API must decide where to place the component between the words/nodes of the formatted text (HTML).
Ex :
<p> Lorem ispum <span>some text</span>
loreb bis <span>ipsum</span>
<MyFeedbackButton paragraph="1.3"/>
</p><p>Other Lorem Ipsum<p><span>...</span>
There, I'm stucked because I cannot use dangerouslySetInnerHTML if there are components inside the loaded HTML...
First attempt : I've tried modifying the API, and instead of sending the HTML in a string to the app, I send a custom JSON structure that represents almost the final JSX structure that I want. Then, in my react page, the render function only have to parse the JSON and build the JSX (here, a JsFiddle example if it's not clear : https://jsfiddle.net/damienfa/69z2wepo/34536/ )
It works, but I can't believe it's the good way...
I see a major problem : all the HTML node (span, p...) that I build from the render function are referenced by reactJs, is it really necessary ? Mostly, there are "dead" nodes (I mean, dom node that won't never changed, this is static formatted text).
Just take a look a all those "data-reactid" on nodes that never will be interactive...
What would be your advice on that case ?
What about my attempt with a JSON-structure sent by the API ?
Is there a way to say to react "do not reference that element" ?
Do you clearly see a better solution to my problem ?
Your current workflow is not very secure and subject to many potential errors and open doors, especially concerning code injection ...
The overload due to react tracking the nodes is not an issue, React could track 10 000 nodes and not have a problem (well actually on many of my apps React has more than 100 000 nodes to care about and it still rurns perfectly).
I see different solutions here:
If there are only 3 or 4 possibilities of dynamic components and order, you might have components like "templates" to which you would simple send text arguments. This is the safest and easiest option.
If it doesn't suit your use-case but the JSON file can contain only a limited set of components, the components should be located in your main app, and then rendered with custom props from the JSON. Actually given the structure of data you could consider using xml instead of json and build a xml tree that you would parse and render. Only components from your white list would be rendered and it would limit drastically the potentials security issues. If needs quite some work on the XML parser though.
If the JSON file can contain many many different and unpredictable components or if the behaviour of those components is largely dynamic and independant of your app, you might as well consider using an iframe, with its own JS and HTML, so that this part of the code is isolated from the rest.
Try using an inline anonymous function within the inner content from within React using JSX. It works! Just be careful about how you wire up the data so there isn't a route where a user can inject HTML from an input or text field.
<div className="html-navigation-button">{(() =>
{
const CreateMarkup = ( sNavItemName :string ) => {
return {__html: sNavItemName };
}
var sTextToAddHtmlTo = props.nextNavItem.name.toString();
sTextToAddHtmlTo = sTextToAddHtmlTo.replace( "/", "/<wbr>" );
return (
<div dangerouslySetInnerHTML={CreateMarkup( sTextToAddHtmlTo )} >
</div>
);
})()}
</div>
I didn't override the React internals of 'render()', but only used a React Component with props wiring to pass down data to it for rendering.
I added the hook for 'dangerouslySetInnerHTML' deep within the return content of the React Component so there would be no easy way to intercept and manipulate it.
As such, there is no 100% guarantee on safety, but that's where adding good security to web services, databases, and use of CORS and CORB would be helpful to lock down security risks.

Resources