reactjs dangerouslySetInnerHTML and dynamically adding classes to links - json

I'm new to reactjs and working on a project that is pushing json data to the template.
json structure
"description" : "Some text with a link and another link",
I propose using the following on the template
<p className='paragraph-margin-bottom-10 text--font-size-14 paragraph--justified' dangerouslySetInnerHTML={{ __html: lang.privacy[0].description }} />
but in terms of the output - I would maybe need to append a set of classes to ALL links. What is the best practice for this
so the links render with the following
<a class="text--font-size-14 hyperlink-primary" href="#">link</a>

I can imagine that many people will not agree with me. You can actually do this. But you shouldn't. It is bad enough that you want to use dangerouslySetInnerHTML. It is possible to parse html but there are many edge cases that you would need to handle.
Either tell your backend that they should return the links with proper classes or target the links inside the description directly with css.
See some similar question like: Using regular expressions to parse HTML: why not?
Using regular expressions to parse HTML: why not?
This is how I would do it. I will write the regex later if you run into some problems. I don't have much time to spare right now. Hope it will help. :)
import React from 'react';
import { render } from 'react-dom';
const htmlFromApi = 'some html from API'
const attachClassesToLinks = (htmlWithLinks) => {
// do something special
return htmlWithLinks
}
const App = () => (
<div>
<h1>My Component</h1>
<p dangerouslySetInnerHTML={{ __html: attachClassesToLinks(htmlFromApi) }} />
</div>
);
render(<App />, document.getElementById('root'));

Related

Why does react not compile HTML present in a state

I set a default value of a state to be <b> Hey </b> . Now when I rendered this state on the UI it printed the string instead of Hey wrote in bold.I want to know why it is not working. Why react is not able to interpret the html tag and show the appropriate output
import { useState } from "react";
import "./styles.css";
export default function App() {
const [html, setHtml] = useState("<b>Hey</b>");
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<div>{html}</div>
</div>
);
}
Output :-
Was expecting the output to be Hey written in bold.
Here's the codesandbox link for better understanding :- https://codesandbox.io/s/heuristic-chaum-vo6qt?file=/src/App.js
Thank you. I just want to know why react is not able to render the HTML tag as HTML tag instead of printing it out.
Because you are rendering a string, not HTML. If you want to render stringified HTML then use dangerouslySetInnerHTML, use caution what you pass through, in other words, you may want to run the string through a DOM purifier first.
export default function App() {
const [html, setHtml] = useState("<b>Hey</b>");
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<div dangerouslySetInnerHTML={{ __html: html}} />
</div>
);
}
You are setting the value of html as "<b>Hey</b>" which is a string string that's why it renders that as it is. You can directly assign html to the variable like so:
const [html, setHtml] = useState(<b>Hey</b>);
It's a string and not HTML, to fix that maybe you can insert it in the div as innerHTML ie.
document.querySelector(".divClassName").innerHTML = html

How to make a textarea take markdown properties (HTML)

How do I use markdown properties in a HTML text area, a little like stack overflow's body area?
Of course I have my standard text area made out like this:
<textarea className="form__input form__group"
value={body}
onChange={e => setBody(e.target.value)} />
But I would like to be able to use markup/markdown tags in my text area to then get an output for when the post is made.
for example:
# Hello there,
> I want to be able to make mytext area be able to take this and then output markdown style body.
Hello there,
I want to be able to make mytext area be able to take this and then output markdown style body.
I would suggest using a library to assist with the markdown. I have used this one before and it works quite well: https://uiwjs.github.io/react-md-editor/
install: npm i #uiw/react-md-editor
Import into react: import MEditor from "#uiw/react-md-editor";
Then just use the MEditor and MEditor.markdown components to input and show your markdown.
There is way more info in the docs so just read https://uiwjs.github.io/react-md-editor/
I also use #uiw/react-md-editor as Croc suggested.
I would also recommend taking care of malicious scripts that untrusted bad actors/users may abuse.
Unfortunately, that's not well documented in the library documentation so here's how you may want to do it:
In edit mode:
<MDEditor
value={value}
autoFocus={false}
onChange={handleChange}
previewOptions={{ skipHtml: true, escapeHtml: true, transformLinkUri: null, linkTarget: '_blank' }}
/>
In preview/read-only mode:
<MDEditor.Markdown source={value} escapeHtml={true} skipHtml={true} transformLinkUri={null} />
Please try this code, To How to make a textarea take markdown properties (HTML).
<form>
<input name="title" type="text" placeholder="Title?" />
<textarea name="content" data-provide="markdown" rows="10"></textarea>
<label class="checkbox">
<input name="publish" type="checkbox"> Publish
</label>
<hr/>
<button type="submit" class="btn">Submit</button>
</form>
I hope this code will be useful to you.
Thank you.
I use react-markdown-editor-lite in most of my projects.
// import react, react-markdown-editor-lite, and a markdown parser you like
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import MarkdownIt from 'markdown-it'
import MdEditor from 'react-markdown-editor-lite'
// import style manually
import 'react-markdown-editor-lite/lib/index.css';
// Register plugins if required
// MdEditor.use(YOUR_PLUGINS_HERE);
// Initialize a markdown parser
const mdParser = new MarkdownIt(/* Markdown-it options */);
// Finish!
function handleEditorChange({html, text}) {
console.log('handleEditorChange', html, text)
//You can use the text props to save it to your database
}
export default (props) => {
return (
<MdEditor
style={{ height: "500px" }}
renderHTML={(text) => mdParser.render(text)}
onChange={handleEditorChange}
/>
)
}

React dangerouslySetInnerHTML not working when using variable

I'm creating an SPA using React that searches data and displays results. Each result follows the following model
{
"title": "A Title",
"body": " <li>escaped html&nbsp;<strong>that sould be rendered</strong>.</li>
</ul>"
}
The body property is always an escaped html that should be rendered in a component. This component looks like this:
Code
function SearchResult({ title, body, favourite }) {
return (
<article className="SearchResult">
<section>
<i className={`icon-star${favourite ? ' marked' : ''}`} />
{title}
</section>
<section
dangerouslySetInnerHTML={{ __html: body }}
className="SearchResult-body"
/>
</article>
);
}
but the body of each result is not being rendered correctly, instead, it shows the html as a text
The issue is that it only happens when I create the component passing a variable to the body property
results.map((result, index) => (
<SearchResult key={index} title={result.title} body={result.body} />
))
But if I do this, it works fine
<SearchResult
title="A title"
body=" <li>escaped html&nbsp;<strong>that sould be rendered</strong>.</li>
</ul>"
/>
Why is this different? Is there any preprocessing that I should add to the value before passing it in the property that is added by default when I use the fixed value?
Demo
A demo of this issue can be seen here
It seems like this issue only occurs when you give it an escaped html.
A solution implemented by #sergiotapia involves creating a helper function to unescape the html string to make it work.
htmlDecode(content) {
let e = document.createElement('div');
e.innerHTML = content;
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}
<section
dangerouslySetInnerHTML={{ __html: htmlDecode(body) }}
className="SearchResult-body"
/>
However as #brigand mentioned and I'll quote "Unescaping it could allow for XSS attacks and incorrect rendering." so this might not be the perfect solution for this.
See working example

How to get the HTML output from a rendered component

I am a newbie to the ReactJS world and trying to get into it. I am working on a style guide for which I need to display some HTML code as an example. I am using ReactPrism for that and I am not able to get the HTML output inside my PrismCode component, I have find a work around by using react-to-jsx which shows the JSX code instead of HTML.
This is the code:
import React from 'react';
import {PrismCode} from "react-prism";
import reactToJsx from 'react-to-jsx';
class CodePreview extends React.Component {
render (){
return (
<div>
{this.props.children}
<h5>Code example</h5>
<pre>
<PrismCode className="language-javascript">
{reactToJsx(this.props.children)}
</PrismCode>
</pre>
</div>
);
}
}
export default CodePreview;
So basically I want to render this.props.children (the component) as HTML code and not the content of it in the PrismCode
I even tried the following as shown on https://github.com/tomchentw/react-prism, but it doesn't work. Not sure what I am doing wrong!
<PrismCode className="language-javascript">
{require("raw-loader!./PrismCode")}
</PrismCode>
Have you considered writing your docs in markdown? I added some special tags for react:
```react:mirror
<Slider
value={7}
/>
```
This will show the rendered component and also the JSX syntax highlighted.
```react:demo
<PropsEditor>
<Slider
value={7}
/>
</PropsEditor>
```
This will render the component as well as a live editor to manipulate any props on the component.
```react
<SomeComponent />
```
Will just syntax highlight but not render the component.
At the top of my markdown file I can import any components I am using in the doc:
---
imports:
- import Slider from '../src/slider'
- import PropsEditor from 'props-editor'
---
The advantage of this way is that your docs work as normal markdown and it's easy to get the JSX as you have it as a string.
To get the HTML source I have a "View Source </> button which prints formatted html dynamically when clicked:
The steps are:
on click get the html of the react component
format the html using prism and a beautifier
insert it into the DOM
So wrap your react component and make a reference to the node:
<div ref={(n) => (this.fenceView = n)}>
And on click add the output below the component, relevant bits:
import prismjs from 'prismjs';
import beautify from 'xml-beautifier';
const RE_HTML_COMMENTS = /<!--[\s\S]*?-->/g;
removeCodeSource() {
const existingHtmlCode = this.fenceView.querySelector('.fence-generated-html');
if (existingHtmlCode) existingHtmlCode.remove();
}
renderCodeSource() {
const html = this.fenceView.children[0].innerHTML.replace(RE_HTML_COMMENTS, '');
const fenceCode = beautify(html, ' ');
const highlightedCode = prismjs.highlight(fenceCode, prismjs.languages.html);
this.removeCodeSource();
this.fenceView.insertAdjacentHTML('beforeend',
`<pre class="fence-generated-html language-html"><code>${highlightedCode}</code></pre>`);
}

How to recognize response as JSON?

In my ReactJS, I'm making a fetch to an API, and the JSON body response field is the following:
{
"place": <a href=\"http:\/\/place.com\/
search?q=%23MILKYDOG\" target=\"_blank\">#milkydog<\/a>
and quickly came up with a little comic about it. You can
(and should) follow Naomi on twitter <a href=\"http:\/\
/david.com\/ngun\" target=\"_blank\">#ngun<\/a> "
}
And when I try to render it by simply passing the prop down (fetched) to rather than recognizing the formatting and escape characters, and rendering accordingly to them.
What's happening here is that React is helping you out by not allowing random HTML to be injected in to your app. You'll need to use the dangerouslySetInnerHTML way of injecting the content. This is made difficult on purpose because React is trying to help you avoid XSS attacks. So you have to do this the "dangerous" way so you think about whether you could do this without injecting raw HTML (it looks like you're getting content from a blog system maybe? Probably a good example of the exception to the rule.)
From the React documentation, you can use something like the following:
function createMarkup() {
return {__html: 'First ยท Second'};
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
e.g. in your case
var fetchedFile = {
"body": `<p><img src=\"http:\/\/media.tumblr.com\
/tumblr_lh6x8d7LBB1qa6gy3.jpg\"\/><a href=\"http:\/\
/citriccomics.com\/blog\/?p=487\" target=\"_blank\">TO READ
THE REST CLICK HERE<\/a><br\/>\n\nMilky Dog was inspired by
something <a href=\"http:\/\/gunadie.com\/naomi\"
target=\"_blank\">Naomi Gee<\/a> wrote on twitter, I really
liked the hash tag <a href=\"http:\/\/twitter.com\/
search?q=%23MILKYDOG\" target=\"_blank\">#milkydog<\/a>
and quickly came up with a little comic about it. You can
(and should) follow Naomi on twitter <a href=\"http:\/\
/twitter.com\/ngun\" target=\"_blank\">#ngun<\/a> `
};
function createMarkup(html) {
return {__html: html};
}
class MyThing extends React.Component {
render() {
return(
<div dangerouslySetInnerHTML={createMarkup(fetchedFile.body)} />
);
}
}
ReactDOM.render(<MyThing />, document.getElementById('content'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="content"></div>
React by default always renders as textNode for javascript variables for security reasons, unless you mention it explicitly.
render() {
return(
<div dangerouslySetInnerHtml={{__html: fetchedFile.body}} />
)
}
Read more on this here