How to use dangerouslySetInnerHTML without the wrapper div? - reactjs

My HTML content is coming from an API (Gatsby in this case) so I'm using dangerouslySetInnerHTML as recommended. The thing is, it messes with my styling, specifically with grids. I have a html markup like this:
<article>
<h2>Title of the post<h2>
<small>Date of the post</small>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</article>
This <article> tag contains a display: grid style. But all the content inside that div is taking precious space making it hard to style (also it's not an useful div!). All the important html is inside but I want to get rid of the actual <div> tag. Is there any way to do it?
Note: I already tried to {post.html} it directly but it's encodedURI which can't be decoded.
Thank you!

Check out the react docs for dangerouslySetInnerHTML.
you can set HTML directly from React, but you have to type out dangerouslySetInnerHTML and pass an object with a __html key...
So there's nothing stopping you from forming your own html string from different parts of your graphql query like so:
const createFullPostMarkup = () => {
return { __html: `<h2>Title of the post</h2><small>Date of the post</small>${ post.html }` }
}
And then setting it later as inner html on your article like this:
<article dangerouslySetInnerHTML={createFullPostMarkup()} />
Remember, this is just an object with a __html key. You can put anything in there.
Note: What I think you might be after is described by this open feature request. For your use case, i think the above solution works perfectly. But if you're still not satisfied, check out the discussion in the linked issue.

You can also do this:
/**
* Load CSS file in parallel vs a blocking <link /> tag
*
* #param {string} fontUrl The raw font URL
*/
const LoadCSSFile = ({ fontUrl }) => {
// hilariously bad hack to get around React's forced wrapper for dangerouslySetInnerHTML
// #see https://github.com/facebook/react/issues/12014#issuecomment-434534770
let linkStr = '</style>';
linkStr += `<link rel="preload" href="${fontUrl}" as="style" onload="this.rel='stylesheet'" />`;
linkStr += `<noscript><link rel="stylesheet" href="${fontUrl}"></noscript>`;
linkStr += '<style>';
return <style dangerouslySetInnerHTML={{ __html: linkStr }} />;
};
This will output as:
<style></style>
<link ... />
<noscript><link ... /></noscript>
<style></style>
Definitely not the most elegant solution but it should accomplish what you need until React supports dangerouslySetInnerHTML on fragments.

Related

Any more or less official way to use class instead of className attribute in React

I wonder if there's more or less official way to use class attribute instead of className in React.
And use normal names in style attribute {{ 'background-color': 'red' }} instead of its camel cased version {{ backgroundColor: 'red' }}.
Ideally with TypeScript support.
This behaviour is already supported in React 16 onwards, maybe not quite in the way you are expecting in your question. The style will not be processed as an JSX but as a string. In 15 the class attribute was ignored while in 16 it will be passed on
Refer the documentation for an explanation why this was changed.
https://reactjs.org/blog/2017/09/08/dom-attributes-in-react-16.html
Below snippet uses React 16
function MyComponent(props){
return <div class="myclass">Color Will be Red</div>
}
ReactDOM.render(
<MyComponent />,
document.getElementById("react")
);
<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>
<style>
.myclass {
color: red
}
</style>
<div id="react"></div>
Below snippet uses React 15
function MyComponent(props){
return <div class="myclass">Color Will not be Red</div>
}
ReactDOM.render(
<MyComponent />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.min.js"></script>
<style>
.myclass {
color: red
}
</style>
<div id="react"></div>
You can already use kebab case (e.g. 'background-color') in JSX style tags without issue, but using class in lieu of className is strongly discouraged in React.
The reason for the latter is that although the JSX your React components looks similar to HTML, it is actually an extension of JavaScript. Because class is a keyword in JavaScript, React throws a warning when you use it in JSX:
Warning: Invalid DOM property class. Did you mean className?
If you still want to use class for CSS class names, you can use the dangerouslySetInnerHTML attribute to set HTML from JSX. Use of this attribute isn't recommended for most use cases including yours, but it won't throw a warning:
const DivWithInnerHTML = () => (
<div
dangerouslySetInnerHTML={{
__html: `
<div class="css-class-name" style="background-color: red;">
html text
</div>
`
}}
/>
);

how to convert props content which has html tags html and display it instead of displaying it as string?

Need to display the html content from props.
var templateData="<div><div>some text</div><div>some more text</div></div>"
<Modal content={templateData} />
In the calling component {content} is only displaying the html tags but isn't rendering.
If you pass it like var templateData="..." it will always be an string.
What you need to do is pass it like var templateData=<div><div>some text</div><div>some more text</div></div> and then render it with < />
e.g.
<Modal content={templateData} />
And inside Modal
let Content = this.props.content // or in props.content in functional components
return (
<Content />
)
If you are getting the string from a external call and you can't add the html in the code, you can use dangerouslySetInnerHTML like Anil Kumar said, but this is very dangerous and not recommended
You can use below snippet:
<div dangerouslySetInnerHTML={{ __html: templateData }} />

`rexxars/react-markdown` plugin usage or alternative for rendering React markdown

I'm trying to render some html, stored in a DB, and put a component inside.
It'd look like this:
import ReactMarkdown from 'react-markdown/with-html';
const inlineCode = (props) => <Gist id={props.value} />;
const source = '`7df0c9a5d794504a28bd3256b7bf5c4f` <p>asdasdasd</p><h1>title</h1>';
ReactMarkdown is used like this:
<ReactMarkdown source={source} renderers={{ inlineCode }} escapeHtml={false} />
The result is is rendered properly and the block is also, but isn't, the contents are outside of the block.
If I wrap the whole source with a <div>, the <Gist/> is rendered as text and <p>/<h1> are rendered properly.
What am I missing? I'm trying to store html with custom components inside, <Gist/> is just an example. Suggestions for a (more) suitable library are also welcome. Example ideal source I'd like to store in a db and then render in a React component:
<div>
<p>
<CustomReactComponent/>
<br/>
test
</p>
<Gist/>
</div>
Okay I found this lib: https://github.com/probablyup/markdown-to-jsx
If your source looks like:
const source = `<gist id="yourIdHere" /> <h1>asdasdasd</h1>`;
<Markdown
options={{
overrides: {
gist: {
component: renderGist,
},
},
}}
>
{content}
</Markdown>
It renders both the <Gist> and the normal <h1> as <h1. Paragraph tags seem to be automatically added if you add line breaks and something like # Title is also automatically wrapped.
<Gist> in source is converted to lowercase, so it'd only matter for the overrides object (lowercase again). Handles all my custom stuff and html predictably.

Display React component Source code using <code> tag

I am creating a react styleguide. I am not using any markdown.I want to display component, description, source code of the component. I can display them using code tag if I keep the entire source code in a variable and display them. (see below)
render() {
var text = `<h1>Helllooo</h1>`;
return (
<div>
<pre>
<code>
{text}
</code>
</pre>
</div>
);
}
But is there any way I can get the source code using a url and display inside code tag
for example get the code from components/Button/index.js and display it inside code tag ? Please help!
There are a few libraries that might help you with what you want to do. Try searching for 'jsx to string'.
I've personally used react-element-to-jsx-string and it worked pretty well.
What you are essentially trying to do here is to render HTML markup using React. Your markup can still be in string format, but ideally, they should be received via props and not be hardcoded in a component.
React by default offers dangerouslySetInnerHTML as a way to accomplish this. But that is not recommended due to security reasons - https://reactjs.org/docs/dom-elements.html
As Grandas suggested, try using a library or a plugin to do the same. html-react-parser is one alternative I could think of - https://github.com/remarkablemark/html-react-parser
You can try storing it in array instead of Sting
You can visit https://jsfiddle.net/rajatdhoot/ara1mdam/1/
to run the code
class Hello extends React.Component {
render() {
var text = [<h2>Hello</h2>]
return text
}
}
ReactDOM.render(
<Hello />,
document.getElementById('container')
);
Html Code
<div id="container">
</div>

How can I render a <template /> tag with react?

Sometimes you might need to render web-components from your react app.
Web-components often use a special <template> ... </template> tag.
But if I try to render such markup with react like this:
render() {
return (
<template>
<div>some content</div>
</template>
)
}
then my web-components don't work correctly.
The reason is that JSX does a different job than what the <template /> tags exists for. The idea of a template tag is to not render its children and pretty much handle it like unparsed text (the browser actually parses it just to make sure its valid html, but does nothing more)
But when you write this in JSX:
return (
<template>
<div>some content</div>
</template>
)
you're basically instructing react to create a 'template' element and then create a 'div' element and then to append this div to the template as a child.
So under hood this happens:
const template = document.createElement('template')
const div = document.createElement('div')
const text = document.createTextNode('some text')
div.appendChild(text)
template.appendChild(div)
But what you want is to set the contents of the <template /> as a string. You can use innerHTML for that.
Solution
One solution would be:
render() {
return (
<template
dangerouslySetInnerHTML={{
__html: '<div>some content</div>'
}}
/>
)
}
Now you're asking react to create all those children tags as node elements but letting the browser decide what to do with them.
Nicer solution
You might not want to use dangerouslySetInnerHTML all the time. So let's create a helper component:
function Template({ children, ...attrs }) {
return (
<template
{...attrs}
dangerouslySetInnerHTML={{ __html: children }}
/>
);
}
Now any time you need to use a template you can use it like this:
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
Don't forget to put the inner content in quotes, because it should be a string.
I know that this question has already an answer, but there is another, I guess simpler solution to do that creating hoc (Higher Order Component).
Just create new "component" like this:
// hoc/Template.js
const template = props => props.children
export default template
and then you can use it in your project this way:
import './hoc/Template.js'
...
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
Newer version of react has already build it such a component, so you can achieve same thing without creating component.
import { Fragment } from 'react'
...
render() {
return (
<Fragment>
{'<div>some content</div>'}
</Fragment>
)
}
This is actually a bug in React! https://github.com/facebook/react/issues/19932
When the native browser parses the <template> tag, it appends all children to the template's content fragment, instead of adding them as children. So a template should never actually have any children.
However, if you programmatically build the DOM (the way react-dom does), then the template will have an empty content fragment, since all children are added as children. This is why web components will not behave properly with these templates.
Here's an example of the problem too: https://codesandbox.io/s/new-sun-8l62o?file=/src/App.tsx:1056-1118
The easiest workaround is to use dangerouslySetInnerHTML, like so:
<template dangerouslySetInnerHTML={{ __html: `
<p> Some text </p>
` }} />
This is limited by the fact that you can only supply raw HTML (no custom React elements). However, since you're using the <template> tag in the first place, it seems highly unlikely that you'd also be using React within the template.

Resources