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
Related
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 have a component called Button.js that has a button that when clicked i simply would like to know if i am accessing the a div in another component called Timer.js. In vanilla javascript i would simply use document.getElementById() to capture the DOM node. How is this done in React?
I came across callback-refs in the docs but it isn't working. If using a ref isn't the React way of accessing DOM elements please refer me to the best way to do this. thanks in advance.
Button.js
function Button() {
const getHtml = () => {
const node = test.current;
console.log(node);
}
return (
<button onClick={getHtml}>GetHtml</button>
)
}
Timer.js
function Timer() {
const test = useRef(null);
return (
<div ref={test}>... </div>
<Button />
}
I would not use a reference to check if a component is rendered inside of another one.
You could get what you're looking for with createContext and useContext.
(It could work like you tried it. If you'd pass the ref to the button as a prop.)
With the context: You create a TimerContext.Provider in your Timer component and in your button you can check with useContext(TimerContext) if the expected key is in the object. If it's not there then the button is not inside of your Timer.
Please have a look at the snippet below or in the following Codesandbox.
//import React, { useContext, createContext } from "react";
//import "./styles.css";
const { useContext, createContext } = React;
const ContainerContext = createContext({
isInContainer: null
});
const Container = () => {
return (
<ContainerContext.Provider value={{ isInContainer: true }}>
<p>
In container:
<Button />
</p>
</ContainerContext.Provider>
);
};
const Button = () => {
const { isInContainer } = useContext(ContainerContext);
console.log(isInContainer);
const isInside = () => {
alert(isInContainer ? "clicked inside" : "not in container");
};
return <button onClick={isInside}>Click me</button>;
};
function App() {
return (
<div className="App">
<Container />
<Button />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
<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 id="root"></div>
Update 15.04.2020
The question was not clear to me at first but now I understand the use-case. The idea is to have an Editor component where you're writing markup that can be used to generate a copied snippet view and/or a html markup output.
For this the best is to use a reference to the Editor component and pass it as prop to the preview/output component - it would be also possible with a context but passing it is easier.
Like in the following Sandbox.
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!
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!