Hello I am working on a blog post creation tool and I need a engine when I write in textarea <hr/> I get a line, or when I write <img/> I get an image but it doesn't render.
The post is written in a textarea and should be displayed in a div.
How to do it?
const PostCreate = () => {
const [postValue, changeValue] = useState('')
const handleChangeValue = (e:any) => {
changeValue(e.target.value)
console.log(postValue);
}
return (
<div className='postCreate'>
Create New Post
<textarea onChange={handleChangeValue} value={postValue}/>
<div>
{postValue}
</div>
</div>
)
}
If I write <hr/> I get the string <hr/> instead of a line:
You can use dangerouslySetInnerHTML prop for the div.
<div dangerouslySetInnerHTML={{ __html: postValue }} />
You can check it on this document.
Related
let htmlFromBackend = `Some backend HTML in table format`
return (
<div dangerouslySetInnerHTML={{ __html: htmlFromBackend}}></div>
)
I want to add some buttons(I want to add a button in every <tr></tr>) and elements in the html getting from backend. How it can be acheived?
For the simplest solution, you can prepend the HTML directly. For example:
return (
<div dangerouslySetInnerHTML={{ __html: `<button onclick="alert('Nice!')">Alert</button>${htmlFromBackend}`}}></div>
)
To prepend buttons on multiple elements of the HTML, you can either use RegExp or parse it first. I'll show you an example that parses the HTML first and edit it.
function preprocessHTML(html){
const element=document.createElement('div');
element.innerHTML=html;
// do any operations you like, for example:
for(const child of element.children){
const btn=document.createElement('button');
btn.innerText='Click Me!';
child.prepend(btn);
}
return element.innerHTML;
}
return (
<div dangerouslySetInnerHTML={{ __html: preprocessHTML(htmlFromBackend)}}></div>
)
If you want to prepend with React, you can use ReactDOM.createPortal to render any JSX element to any HTMLDom.
I have a problem which requires me to store the texted of a referenced element in an array.
Now, I first want to display the text for each element(paragraph element with "ebookName" class) in the console, before storing it in the array.
But I have been having problems... Whenever I click an element, the console just logs the previous elements text always. I want for each paragraph element to log that specific elements text, not the previous one
Link to JS code:
import React from 'react'
import "./Styles/Ebook.css"
import { useRef } from 'react';
function Ebook() {
const bookName = useRef();
let ebookData = JSON.parse(sessionStorage.getItem("ebook"));
/*function that displays the specific text of a specific element onto the console*/
const elementLogFunction = () =>{
console.log(bookName.current)
}
return (
<section id="musicRender">
{ebookData.results.map((ebook, i)=>{
return (
<div key={i} className='ebookContentContainer'>
<div className="ebookPicture">
<img src={ebook.artworkUrl100} alt={ebook.trackName} />
</div>
<div className="ebook-description">
<p className="ebookAuthor">Author: {ebook.artistName}</p>
<p ref={bookName} className='ebookAName'>Book Name: {ebook.trackName}</p>
<p className="price">Price: R{(ebook.price * 15.36).toFixed(0)}</p>
<button onClick={elementLogFunction} className="favourites-btn">Add To Favourites</button>
</div>
</div>)
})}
</section>
)
}
export default Ebook
According to your code, ref is only referred to the same data, and the new one will override the old one. In your case, the last book data will be kept.
If you want to have individual book data separately, you can pass a param to elementLogFunction.
You also shouldn't read sessionStorage every rendering. This behavior causes a performance issue due to getting data multiple times. You can use useEffect to read data only once after the first rendering.
function Ebook() {
const [ebookData, setEbookData] = React.useState([]);
//only add data for the first rendering
useEffect(() => {
setEbookData(JSON.parse(sessionStorage.getItem("ebook")));
}, []);
/*function that displays the specific text of a specific element onto the console*/
const elementLogFunction = (ebook) =>{
console.log(ebook.trackName)
}
return (
<section id="musicRender">
{ebookData.results.map((ebook, i)=>{
return (
<div key={i} className='ebookContentContainer'>
<div className="ebookPicture">
<img src={ebook.artworkUrl100} alt={ebook.trackName} />
</div>
<div className="ebook-description">
<p className="ebookAuthor">Author: {ebook.artistName}</p>
<p ref={bookName} className='ebookAName'>Book Name: {ebook.trackName}</p>
<p className="price">Price: R{(ebook.price * 15.36).toFixed(0)}</p>
<button onClick={() => elementLogFunction(ebook)} className="favourites-btn">Add To Favourites</button>
</div>
</div>)
})}
</section>
)
}
export default Ebook
what is a valid approach to visualize a text created inside a textarea element with the correctly assigned line breaks?
As soon as I input a line break by pressing Enter key, I can see that this is displayed inside the textarea correctly but when I visualize the output in another element, the line break appears as a space. The data is also saved in my database without any line breaks.
I have attached a sample code to the link below:
https://codesandbox.io/s/busy-hodgkin-1oujz?file=/src/App.js
The easiest way would be through CSS, by following:
...
<div className="App">
<div className="wrapper">
<div>
<p class='formatted'>{text}</p>
</div>
...
And then: .formatted { white-space: pre; }
Another way would be using dangerouslySetInnerHTML:
...
<div className="wrapper">
<div>
<p
dangerouslySetInnerHTML={{
__html: formattedText,
}}>
</p>
</div>
...
And the logic to handle that would be:
const [text, setText] = useState('');
const [formattedText, setFormattedText] = useState(text);
const handleTextInput = ({ target }) => {
const { value } = target;
const formattedValue = value.replace(/\n/g, '<br />');
setText(value);
setFormattedText(formattedValue);
};
There's some possible approachs to your problem. In few words, \n new line characters on textarea does not work on plain html. Instead, we could use <br />.
Instead of simply rendering text as it's stored, you could split into new lines and render <br /> in its place:
<p>
{text.split('\n').map((subtext) => (
<>
{subtext}
<br/>
</>
))}
</p>
I have a html string that contains a link. I need to add the attribute rel="noopener" for security purposes. The html string is injected through dangerouslySetInnerHtml:
const Component = ({ message }) => {
return (
<div>
<div dangerouslySetInnerHTML={{ __html: message }} />
<div>
);
};
The string looks like: Hello check out this page
So the desired output would be: Hello check out this page
How to do it?
Try this:
const Component = ({ message }) => {
function secureATags(html) {
// Parse HTML
let doc = (new DOMParser()).parseFromString(html, "text/html")
// Append attribute
doc.querySelectorAll('a').forEach(entry => {
entry.setAttribute('rel', 'noopener')
})
// Reserialize to HTML
return doc.body.innerHTML
}
return (
<div>
<div dangerouslySetInnerHTML={{ __html: secureATags(message) }} />
<div>
)
}
I would use the Browser DOM to achieve this, as follows:
const div = document.createElement("div");
div.innerHTML = 'Hello check out this page';
div.childNodes[1].setAttribute("rel", "noopener");
console.log(div.innerHTML);
If the actual HTML text is more complex than in your example, then div.childNodes[1] will need to be replaced with code that looks for and selects the proper node. But even then (or especially then?), this is probably the easiest and most reliable way to achieve your goal.
Direct use of setDangerousInnerHtml is strictly not recommended due to security issues.
you can use a plugin on npmjs.org pkgname: React-html-parser for injecting the html safely
Maybe consider using a function or component that puts it all together based on the data you send in? E.g.
function App() {
const linkBuilder = (textBefore, linkText, textAfter, href) => {
return (
<div>
{textBefore}
<a href={href} target="_blank">
{linkText}
</a>
{textAfter}
</div>
);
};
return (
<div>
{linkBuilder("Hello check out ", "this page", "", "www.google.com")}
</div>
);
}
<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>
Also according to this you don't need rel=noopener anymore if you use target="_blank". But if it's necessary you can pass it in as a boolean and apply it on the function/component side.
Is it possible to put JSX inside a template string that is being used as a React render prop?
This is what I'm trying to do, but it leads to the link rendering as [object Object]
const Container = ({ message }) => <div className="from line 4"> {message}</div>;
const Link = () => juan;
const App = () => (
<div>
<Container message={`My message with a ${<Link />}`} />
</div>
);
One thing I tried was to put JSX instead of a template string inside message. This works, but it introduces a new div that isn't needed.
<Container
message={<div>My message {<Link />}</div>}
/>
I made this codesandbox to illustrate the problem
You can use a Fragment to render inline like you are trying to do and to prevent adding a new wrapping <div />:
const App = () => (
<div>
<Container
message={<React.Fragment>My message with a <Link /></React.Fragment>}
/>
</div>
);
Here is a forked version of your Codesandbox using React.Fragment: https://codesandbox.io/s/nrmr9l34vl