Here's what I tried and how it goes wrong.
This works:
<div dangerouslySetInnerHTML={{ __html: "<h1>Hi there!</h1>" }} />
This doesn't:
<div dangerouslySetInnerHTML={{ __html: this.props.match.description }} />
The description property is just a normal string of HTML content. However it's rendered as a string, not as HTML for some reason.
Any suggestions?
Is this.props.match.description a string or an object? If it's a string, it should be converted to HTML just fine. Example:
class App extends React.Component {
constructor() {
super();
this.state = {
description: '<h1 style="color:red;">something</h1>'
}
}
render() {
return (
<div dangerouslySetInnerHTML={{ __html: this.state.description }} />
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
Result: http://codepen.io/ilanus/pen/QKgoLA?editors=1011
However if description is <h1 style="color:red;">something</h1> without the quotes '', you're going to get:
Object {
$$typeof: [object Symbol] {},
_owner: null,
key: null,
props: Object {
children: "something",
style: "color:red;"
},
ref: null,
type: "h1"
}
If It's a string and you don't see any HTML markup the only problem I see is wrong markup..
UPDATE
If you are dealing with HTML Entities, You need to decode them before sending them to dangerouslySetInnerHTML that's why it's called "dangerously" :)
Working example:
class App extends React.Component {
constructor() {
super();
this.state = {
description: '<p><strong>Our Opportunity:</strong></p>'
}
}
htmlDecode(input){
var e = document.createElement('div');
e.innerHTML = input;
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}
render() {
return (
<div dangerouslySetInnerHTML={{ __html: this.htmlDecode(this.state.description) }} />
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
I use 'react-html-parser'
yarn add react-html-parser
import ReactHtmlParser from 'react-html-parser';
<div> { ReactHtmlParser (html_string) } </div>
Source on npmjs.com
Lifting up #okram's comment for more visibility:
from its github description: Converts HTML strings directly into React
components avoiding the need to use dangerouslySetInnerHTML from
npmjs.com A utility for converting HTML strings into React components.
Avoids the use of dangerouslySetInnerHTML and converts standard HTML
elements, attributes and inline styles into their React equivalents.
Check if the text you're trying to append to the node is not escaped like this:
var prop = {
match: {
description: '<h1>Hi there!</h1>'
}
};
Instead of this:
var prop = {
match: {
description: '<h1>Hi there!</h1>'
}
};
if is escaped you should convert it from your server-side.
The node is text because is escaped
The node is a dom node because isn't escaped
If you have HTML in a string I would recommend using a package called html-react-parser.
Installation
NPM:
npm install html-react-parser
yarn:
yarn add html-react-parser
Usage
import parse from 'html-react-parser'
const yourHtmlString = '<h1>Hello</h1>'
code:
<div>
{parse(yourHtmlString)}
</div>
If you have control over where the string containing html is coming from (ie. somewhere in your app), you can benefit from the new <Fragment> API, doing something like:
import React, {Fragment} from 'react'
const stringsSomeWithHtml = {
testOne: (
<Fragment>
Some text <strong>wrapped with strong</strong>
</Fragment>
),
testTwo: `This is just a plain string, but it'll print fine too`,
}
...
render() {
return <div>{stringsSomeWithHtml[prop.key]}</div>
}
I use innerHTML together a ref to span:
import React, { useRef, useEffect, useState } from 'react';
export default function Sample() {
const spanRef = useRef<HTMLSpanElement>(null);
const [someHTML,] = useState("some <b>bold</b>");
useEffect(() => {
if (spanRef.current) {
spanRef.current.innerHTML = someHTML;
}
}, [spanRef.current, someHTML]);
return <div>
my custom text follows<br />
<span ref={spanRef} />
</div>
}
UPDATE:
I removed someHTML state and added comments to make the example more coincise around the concept.
/**
* example how to retrieve a reference to an html object
*/
import React, { useRef, useEffect } from 'react';
/**
* this component can be used into another for example <Sample/>
*/
export default function Sample() {
/**
* 1) spanRef is now a React.RefObject<HTMLSpanElement>
* initially created with null value
*/
const spanRef = useRef<HTMLSpanElement>(null);
/**
* 2) later, when spanRef changes because html span element with ref attribute,
* follow useEffect hook will triggered because of dependent [spanRef].
* in an if ( spanRef.current ) that states if spanRef assigned to valid html obj
* we do what we need : in this case through current.innerHTML
*/
useEffect(() => {
if (spanRef.current) {
spanRef.current.innerHTML = "some <b>bold</b>";
}
}, [spanRef]);
return <div>
my custom text follows<br />
{/* ref={spanRef] will update the React.RefObject `spanRef` when html obj ready */}
<span ref={spanRef} />
</div>
}
You just use dangerouslySetInnerHTML method of React
<div dangerouslySetInnerHTML={{ __html: htmlString }} />
Or you can implement more with this easy way: Render the HTML raw in React app
In my case, I used react-render-html
First install the package by npm i --save react-render-html
then,
import renderHTML from 'react-render-html';
renderHTML("<a class='github' href='https://github.com'><b>GitHub</b></a>")
I could not get npm build to work with react-html-parser. However, in my case, I was able to successfully make use of https://reactjs.org/docs/fragments.html. I had a requirement to show few html unicode characters , but they should not be directly embedded in the JSX. Within the JSX, it had to be picked from the Component's state. Component code snippet is given below :
constructor()
{
this.state = {
rankMap : {"5" : <Fragment>★ ★ ★ ★ ★</Fragment> ,
"4" : <Fragment>★ ★ ★ ★ ☆</Fragment>,
"3" : <Fragment>★ ★ ★ ☆ ☆</Fragment> ,
"2" : <Fragment>★ ★ ☆ ☆ ☆</Fragment>,
"1" : <Fragment>★ ☆ ☆ ☆ ☆</Fragment>}
};
}
render()
{
return (<div class="card-footer">
<small class="text-muted">{ this.state.rankMap["5"] }</small>
</div>);
}
i use https://www.npmjs.com/package/html-to-react
const HtmlToReactParser = require('html-to-react').Parser;
let htmlInput = html.template;
let htmlToReactParser = new HtmlToReactParser();
let reactElement = htmlToReactParser.parse(htmlInput);
return(<div>{reactElement}</div>)
You can also use parseReactHTMLComponent from Jumper Package. Just look at it, it's easy and you don't need to use JSX syntax.
https://codesandbox.io/s/jumper-module-react-simple-parser-3b8c9?file=/src/App.js .
More on Jumper:
https://github.com/Grano22/jumper/blob/master/components.js
NPM Package:
https://www.npmjs.com/package/jumper_react
// For typescript
import parse, { HTMLReactParserOptions } from "html-react-parser";
import { Element } from "domhandler/lib/node";
export function contentHandler(postContent: string) {
const options: HTMLReactParserOptions = {
replace: (domNode: Element) => {
if (domNode.attribs) {
if (domNode.attribs.id === 'shortcode') {
return <div className="leadform">Shortcode</div>;
}
}
},
};
return parse(postContent, options);
}
// Usage: contentHandler("<span>Hello World!</span>")
If you have control to the {this.props.match.description} and if you are using JSX. I would recommend not to use "dangerouslySetInnerHTML".
// In JSX, you can define a html object rather than a string to contain raw HTML
let description = <h1>Hi there!</h1>;
// Here is how you print
return (
{description}
);
Related
It seems this concept is so basic, there's a lack of documentation about it. I can pass objects as props, but can't seem to pass a basic string literal.
Functional Component
I have a functional component that takes a typed prop, like so:
const ChildComponent = (name: string) => {
return (
<div className={styles.childComponent}>
<p className={styles.styledName}>
{ name }
</p>
</div>
);
}
and call it like so:
<ChildComponent name="testName" />
Error
VSCode throws the error on ChildComponent:
Type '{ name: string; }' is not assignable to type 'string'
I'm very new to Typescript, but from what I can tell, it's reading the string literal as an object.
Possible Solutions
Much of what I've read advises creating a custom typed prop, even for a single property, like so:
Type: NameProp {
name: string
}
and using that as the prop type, instead of string.
Isn't this overkill? Or am I missing something very basic.
const ChildComponent = ({ name }: { name: string }) => {
return (
<div className={styles.childComponent}>
<p className={styles.styledName}>{name}</p>
</div>
);
};
You have to destructure it from props object.
props is an object.
CODESADNBOX LINK
const ChildComponent = (props: ChildComponentProps) => {
const { name } = props; // CHANGE THAT YOU HAVE TO DO
return (
<div className={styles.childComponent}>
<p className={styles.styledName}>{name}</p>
</div>
);
};
or
const ChildComponent = ({ name }: ChildComponentProps) => {
return (
<div className={styles.childComponent}>
<p className={styles.styledName}>{name}</p>
</div>
);
};
where ChildComponentProps is
interface ChildComponentProps {
name: string;
}
Define a prop interface the define the props parameter as object :
interface Props{
name:string
}
const ChildComponent: React.FC<Props> = ({name}) => {
return (
<div className={styles.childComponent}>
<p className={styles.styledName}>
{ name }
</p>
</div>
);
}
Props supposed to be an object. You are not the only one using this prop object.
Note that JSX is just a syntax extension use to make it easy to read & code components.
But this code will translate into the pure javascript code,
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
The above code will translate into before execute,
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
Later this createElement will also return an object which will use to build the whole element tree of your application.
// Note: this structure is simplified
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
Notice the children property ? Its also a prop which was not added by you but the React it self.
NOTE: Above code snippets are directly copied from React JS documentation.
So to answer your question,
You cannot change the type of the props object. It must be an object. The reason why you seen that error message is because you forcefully telling the type of props is an string but actually it is not.
The way you should do it is,
const ChildComponent: React.FC<{ name: string }> = ({ name }) => {
return (
<div className={styles.childComponent}>
<p className={styles.styledName}>
{ name }
</p>
</div>
);
}
I am not using React.
I am using Stenciljs.
I have the following .tsx file:
export class MyComponent {
#Prop() message: string;
render() {
return (<div>{this.message}</div>);
}
}
I want to do this instead:
import myTemplate from '../my-template.??';
export class MyComponent {
#Prop() message: string;
render() {
return (myTemplate);
}
}
with ../my-template.?? containing:
<div>{this.message}</div>
Is it possible and how ? Thanks in advance for any help :)
Yes, you can absolutely do this, there are just a couple of things you need to tidy up:
Main file
import { Template } from '../template'; // No need for file extension but we're using a named export so we need the curly braces around 'Template'
export class MyComponent {
#Prop() message: string;
render() {
return ( // You don't technically need the parentheses here as you're just returning one thing
<Template /> // When outputting an imported component, it goes in angle brackets and the backslash closes it like an HTML element
)
}
}
Template
import React from 'react'; // template needs React
export const Template = () => { // defining the export in this way is known as a named export
return (
<p>A message here</p>
)
}
Okay, so that's going to get you a message output which is from your template. However, you were asking about passing a message to that template for it to output. That's totally easy as well - you just need to get some props in there. Here is the modified version of the above:
Main file
import { Template } from '../template';
export class MyComponent {
#Prop() message: string;
render() {
return (
<Template messageToOutput={message} /> // The first argument is the name of the prop, the second is the variable you defined above
)
}
}
Template
import React from 'react';
export const Template = (props) => { // props are received here
return (
<p>{props.messageToOutput}</p> // props are used here
)
}
That's how you pass data around in React - hope that helps!
The server returns something like:
content = <p> Hello world :smile: <strong> NICE </strong> !</p> - this is because we support markdown.
Now I have a parser that parses everything with :{text}: into an emoji. I am using emoji-mart for this one.
So this is what content looks like now:
<p> Hello world ${<Emoji emoji=":smile:" />} <strong> NICE </strong> !</p>
Currently without the emoji parser what we do is:
return React.createElement('div', {
dangerouslySetInnerHTML: {
__html: content,
}
});
However, since we now concatenating the content to contain the Emoji from emoji-mart how will I pass this to dangerouslySetInnerHTML without breaking the markdown?
Upon playing around with the situation I discovered that you can actually pass use functional components and return string instead: https://github.com/missive/emoji-mart#using-with-dangerouslysetinnerhtml (Specific for my problem regarding emoji-mart)
So what I did with my other Components are the same, instead of calling a React component I created a function instead:
function testComponent(props) {
const { style, className, children, html } = props;
if (html) {
return `<span style='${style}' class='${className}'>${children || ''}</span>`;
}
return (
<span style="${style}" class="${className}">
${children || ''}
</span>
);
}
And called it as:
function testComponent(props) {
const { content } = props; // content is a markdown and can be a stringified image tag
return testComponent({ children: content, html: true });
}
And for the dangerouslySetInnerHTML:
(render function inside of your react component)
render() {
const props = {
dangerouslySetInnerHTML: {
__html: testComponent(this.props.content),
},
};
return React.createElement('div', props);
}
This is hackier, but less expensive than using:
renderToString()
renderToStaticMarkup()
You should use React.renderToStaticMarkup(JSXInstance), in your case:
<p> Hello world ${React.renderToStaticMarkup(<Emoji emoji=":smile:" />)} <strong> NICE </strong> !</p>
I've been playing around with draft-js by Facebook, but I can't actually figure out how to get the html output of the editor. The console.log in the following example outputs some _map properties, but they don't seem to contain my actual content?
class ContentContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
editorState: EditorState.createEmpty()
};
this.onChange = (editorState) => this.setState({editorState});
this.createContent = this.createContent.bind(this);
}
createContent() {
console.log(this.state.editorState.getCurrentContent());
}
render() {
const {editorState} = this.state;
const { content } = this.props;
return (
<Template>
<br /><br /><br />
<ContentList content={content} />
<div className="content__editor">
<Editor editorState={editorState} onChange={this.onChange} ref="content"/>
</div>
<FormButton text="Create" onClick={this.createContent.bind(this)} />
</Template>
);
}
}
There is a handy library I used, draft-js-export-html. Import the library and you should be able to see HTML once you invoke the function, stateToHTML:
console.log(stateToHTML(this.state.editorState.getCurrentContent()));
I'm pretty new to React so hopefully this works for you. I looked under the hood of contentState and there is a fair bit going on there that makes using a library to parse out the entities that much more enticing.
The author, sstur, answers a tangentially-related question where I learned about his libraries.
Ewan. I am also playing with Draft.js and came across the same problem. Actually, Victor has provided a great solution.
Here are two libraries that I found. The one mentioned by Victor has more stars on GitHub.
https://github.com/sstur/draft-js-export-html
https://github.com/rkpasia/draft-js-exporter
I just want to add that there is a way to print out the content (in JSON format) without using an external library. It is documented under the Data Conversion session.
Here is how I print out user input using the "convertToRaw" function
console.log(convertToRaw(yourEditorContentState.getCurrentContent()));
Make sure you imported the convertToRaw function from Draft.js by writing:
import { convertFromRaw, convertToRaw } from 'draft-js';
Here is a great blog written by rajaraodv named How Draft.js Represents Rich Text Data. It explained data conversion in detail.
There is readonly attribute to generate just HTML:
<Editor editorState={editorState} readOnly/>
If not willing to add another library to your code, #farincz's approach can work well.
<Editor editorState={this.state.editorState} readOnly/>
The editor state can be directly stored in your storage layer and when you are rendering it to the DOM it is easily available and can help in editing.
By clicking on the text you can make it editable, or bind that click with an edit button. You cannot directly bind click to 'Editor' component, but you can have it on the wrapper containing the 'Editor'.
<div className="editor" onClick={this.editContent.bind(this)}>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
handleKeyCommand={this.handleKeyCommand}
readOnly={this.state.edit}
/>
</div>
Just add 'edit' to your state as true, making sure that readOnly is true (you can make the name 'edit' of the state more obvious, if it is confusing).
this.state = {
editorState: EditorState.createEmpty(),
edit: true
};
Finally change the value of 'edit' to false on click
editContent() {
this.setState({
edit: false
})
}
To expand on the libraries shared above, here's another good one : draftjs-to-html
It converts raw editor state (JSON object) into plain HTML.
import draftToHtml from 'draftjs-to-html';
import {convertToRaw} from "draft-js";
const rawContentState = convertToRaw(editorState.getCurrentContent());
const markup = draftToHtml(rawContentState);
The most suitable package to convert to HTML and from HTML is draft-convert
However, you should be aware to use the Advanced usage to be able to convert links and to customize the convert process:
const htmlResult = convertToHTML({
entityToHTML: (entity, originalText) => {
if (entity.type === 'LINK') {
return <a href={entity.data.url}>{originalText}</a>;
}
return originalText;
}
})(editorState.getCurrentContent());
const contentState = convertFromHTML({
htmlToEntity: (nodeName, node, createEntity): any | void => {
if (nodeName === 'a') {
return createEntity(
'LINK',
'MUTABLE',
{url: node.href}
)
}
}
})(htmlInput);
I have the following react component
homeComponent = React.createClass({
mixins: [ReactMeteorData],
getMeteorData() {
return {
homeData: homeContent.findOne()
}
},
render(){
let homeData = this.data.homeData;
return (
<div className="units-container home-container">
<h1>ply</h1>
<p>{homeData.homeCopy}</p>
Download
</div>
)
}
})
The key {homeData.homeCopy} returns a string that has html tags in. So in this case let's say it returns <strong>bold text</strong> not bold text.
I'm using react-template-helper to use both blaze and react, so I would expect that I could use triple brackets to escape the html, but that's not working. I need to escape the html from react I suppose.
How can I get react to honor these html tags from the string?
#mason505 pointed this in the right direction, but I think this needs some extra information.
First you must make a function to parse the html:
function escapeHTML(data) {
return {__html: data}
}
Then you must call that with dangerouslySetInnerHTML but only on a self enclosed html element and you must pass the data into dangerouslySetInnerHTML using the escapeHTML function. Complete example is:
function escapeHTML(data) {
return {__html: data}
}
homeComponent = React.createClass({
mixins: [ReactMeteorData],
getMeteorData() {
return {
homeData: homeContent.findOne()
}
},
render(){
let homeData = this.data.homeData;
return (
<div className="units-container home-container">
<h1>ply</h1>
<p dangerouslySetInnerHTML={escapeHTML(homeData.homeCopy)} />
Download
</div>
)
}
})