This question already has answers here:
React: leave the contents of a component alone
(2 answers)
Closed 2 years ago.
Is it possible to use dom element(s) as children in JSX when rendering a component? I think this explains what I'm attempting but is invalid.
<div id="container">
<div>node 1</div>
<div>node 2</div>
</div>
const container = document.querySelector('#container');
ReactDOM.render(
<MyComponent>{container.children}</MyComponent>,
container
);
Yes, it is possible using dangerouslySetInnerHTML, but it is not recommended.
From the docs:
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.
const {useState, useEffect} = React;
function App(){
const [html, setHtml] = useState('Loading...');
useEffect(()=>{
setHtml(document.querySelector('#container').innerHTML);
}, []);
return <div dangerouslySetInnerHTML={{__html: html}}/>;
}
ReactDOM.render(
<App/>,
document.querySelector('#app')
);
#container {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.5/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.5/umd/react-dom.production.min.js"></script>
<div id='app'></div>
<div id="container">
<div>node 1</div>
<div>node 2</div>
</div>
Hope this helps!
Related
I have a string about html builded by react.
I'm trying to implement render this html string through react, and looking for solution to manage state.
below is simple example.
const App = (props) => {
let code = '<b>Will This Work?</b>';
return (
<div dangerouslySetInnerHTML={ {__html: code} }>
</div>
);
}
I want to manage the state of component rendered with dangerouslySetInnerHTML option.
Can i get any ideas about how approach?
What is dangerouslySetInnerHTML?
It is a way to set the children of the component (as text/html).
Can state be used in it?
Yes state can be used with it; However, the innerHTML MUST be vanilla HTML, NOT JSX. An example of this can be seen below:
(click the button to change the state from text to a red div)
The way this is working is because the state can be inserted into a string literal which will then be handled as HTML encoded text.
const App = (props) => {
const [state,setState] = React.useState("something I set with state");
let code = `<b>Will This Work? ${state}</b>`;
return (
<React.Fragment>
<button onClick={()=>{setState("<div style=\"background-color: red; width:50px; height:50px;\"><div>")}}>Click to change state from pure text to an html element</button>
<div dangerouslySetInnerHTML={ {__html: code} }>
</div>
</React.Fragment>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.querySelector('.react')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
Should you do this?
Probably not. There is a reason it is called dangerouslySetHTML and not safelySetInnerHTML.
See more in the React Docs
I was just doing some experimentation and I noticed if I have:
const Component1 = () => {
console.log("Component1");
return <div>Component1</div>;
};
const Component2 = () => {
console.log("Component2");
return <div>Component2</div>;
};
Now when using the components like this:
export default function App() {
const [state, setState] = useState(false);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Component1 />
{Component2()}
<button onClick={setState.bind(null, !state)}>Change State</button>
</div>
);
}
I noticed that in console.log component2 appears before component1 even tho they are rendered in the correct order, why does this happen?
JSX elements are syntax sugar for React.createElement. React.createElement does not run the components inside it immediately:
const TheComponent = () => {
console.log('component being created');
};
React.createElement(TheComponent);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
Rather, it waits for the elements to be rendered.
When you do
<Component1 />
{Component2()}
You call Component2 immediately, but since the rendering doesn't occur instantaneously - it occurs a little bit of time later - Component1 takes a bit of time to log.
Note that you should never use
{Component2()}
when rendering an element - always use JSX syntax or React.createElement, as React expects you to do.
The reason for appearing the console.log earlier is that calling the component as a function is much faster than the normal JSX declaration. Research shows it's 45% faster.
Also, it's not a malpractice. It is most suitable with stateless functional components.
Resource - https://medium.com/missive-app/45-faster-react-functional-components-now-3509a668e69f
Let me know if you need further support.
thanks for taking the time to look at this.
I am struggling to find out how to make this work specifically in react.
I have used contentEditable to get the div element to be editable and then i have used Refs to make the div reference its innerHTML. But the information does not seem to be put into the state of body.
The ultimate aim is to have it saved in a database, and then loaded to replace the div.
code:
import React, {useState, useRef} from "react";
import "./styles.css";
export default function App() {
let myRef= useRef()
const [body, setBody] = useState("");
let click = () => {
setBody(myRef.innerHTML)
}
return (
<div className="App">
<h1 ref={myRef}>Hello CodeSandbox</h1>
<div></div>
<h1 contentEditable={true}> rewrite me!</h1>
<button onClick={click}> CLICK!</button>
<h1>{body}</h1>
</div>
);
}
sandbox
https://codesandbox.io/s/wispy-glitter-nfym4?file=/src/App.js
Access the innerHTML using myRef.current.innerHTML.
From the docs
When a ref is passed to an element in render, a reference to the node becomes accessible at the current attribute of the ref.
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/#babel/standalone/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
function App() {
let myRef = React.useRef();
const [body, setBody] = React.useState("");
let click = () => {
setBody(myRef.current.innerHTML);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<div></div>
{/* I think you misassigned your `myRef`, shouldn't it be on this h1? */}
{/* suppressContentEditableWarning=true, to suppress warning */}
<h1 ref={myRef} contentEditable={true} suppressContentEditableWarning={true}> rewrite me!</h1>
<button onClick={click}> CLICK!</button>
<h1>{body}</h1>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
</script>
I'm converting a html string to React element using the DOM parser and render it in my component.
I'm unable to use the dangerouslySetElements method as we are using sanitize html to encode any html tags and hence this usage render raw html with tags.
code:
function() {
const element = (
<div>
<span>test232></span>
<label>ahddgd</label>
</div>
}
<span data-tip-react data-tip = {ReactDOMServer.renderToString(element)}>test</span>
test() {
let section;
tipValue = targetNode.getAttribute("data-tip");
const doc = new DOMParser().parseFromString(tipValue, "text/xml");
section = (doc.childNodes[0]);
}
and this is the o/p I get for section:
now when I try to render this in my component, it throws this error:
render() {
return (
<div>{section}</div>
)
}
Objects are not valid as a React child (found: [object Element]). If you meant to render a collection of children, use an array instead.
Any idea how to convert the above o.p that would render as react comp.? Unfortunately im also unable to use any third party lib..:(
Yes, it is possible using dangerouslySetInnerHTML, but it is not recommended.
From the docs:
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.
Here is a working example how this could be done:
const {useState, useEffect} = React;
function App(){
const [data, setData] = useState("Loading...");
useEffect(()=>{
const containerInnerHTML = document.querySelector('#element').innerHTML;
setData(containerInnerHTML);
},[]);
return <div dangerouslySetInnerHTML={{__html: data}}/>;
}
ReactDOM.render(<App/>,document.getElementById('app'));
#element {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='app'></div>
<div id="element">
<div>node 1</div>
<div>node 2</div>
</div>
Hope this helps!
I read this on the React tutorial. What does this mean?
React is safe. We are not generating HTML strings so XSS protection is the default.
How do XSS attacks work if React is safe? How is this safety achieved?
ReactJS is quite safe by design since
String variables in views are escaped automatically
With JSX you pass a function as the event handler, rather than a string that can contain malicious code
so a typical attack like this will not work
const username = "<img onerror='alert(\"Hacked!\")' src='invalid-image' />";
class UserProfilePage extends React.Component {
render() {
return (
<h1> Hello {username}!</h1>
);
}
}
ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
but ...
❗❗❗Warning❗❗❗
There are still some XSS attack vectors that you need to handle yourself in React!
1. XSS via dangerouslySetInnerHTML
When you use dangerouslySetInnerHTML you need to make sure the content doesn't contain any javascript. React can't do here anything for you.
const aboutUserText = "<img onerror='alert(\"Hacked!\");' src='invalid-image' />";
class AboutUserComponent extends React.Component {
render() {
return (
<div dangerouslySetInnerHTML={{"__html": aboutUserText}} />
);
}
}
ReactDOM.render(<AboutUserComponent />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
2. XSS via a.href attribute
Example 1: Using javascript:code
Click on "Run code snippet" -> "My Website" to see the result
const userWebsite = "javascript:alert('Hacked!');";
class UserProfilePage extends React.Component {
render() {
return (
<a href={userWebsite}>My Website</a>
)
}
}
ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Example 2: Using base64 encoded data:
Click on "Run code snippet" -> "My Website" to see the result
const userWebsite = "data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGFja2VkISIpOzwvc2NyaXB0Pg==";
class UserProfilePage extends React.Component {
render() {
const url = userWebsite.replace(/^(javascript\:)/, "");
return (
<a href={url}>My Website</a>
)
}
}
ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
3. XSS via attacker controlled props
const customPropsControledByAttacker = {
dangerouslySetInnerHTML: {
"__html": "<img onerror='alert(\"Hacked!\");' src='invalid-image' />"
}
};
class Divider extends React.Component {
render() {
return (
<div {...customPropsControledByAttacker} />
);
}
}
ReactDOM.render(<Divider />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Here are more resources
Exploiting Script Injection Flaws in ReactJS Apps
The Most Common XSS Vulnerability in React.js Applications
How Much XSS Vulnerability Protection is React Responsible For?
https://github.com/facebook/react/issues/3473#issuecomment-90594748
https://github.com/facebook/react/issues/3473#issuecomment-91349525
Avoiding XSS in React is Still Hard
Avoiding XSS via Markdown in React
React automatically escapes variables for you... It prevents XSS injection via string HTML with malicious Javascript.. Naturally, inputs are sanitized along with this.
For instance let's say you have this string
var htmlString = '<img src="javascript:alert('XSS!')" />';
if you try to render this string in react
render() {
return (
<div>{htmlString}</div>
);
}
you will literally see on the page the whole string including the <span> element tag. aka in the browser you will see <img src="javascript:alert('XSS!')" />
if you view the source html you would see
<span>"<img src="javascript:alert('XSS!')" />"</span>
Here is some more detail on what an XSS attack is
React basically makes it so you can't insert markup unless you create the elements yourself in the render function... that being said they do have a function that allows such rendering its called dangerouslySetInnerHTML... here is some more detail about it
Edit:
Few things to note, there are ways to get around what React escapes. One more common way is when users define props to your component. Dont extend any data from user input as props!