Visualize onkeypress line breaks in textarea with REACT - javascript

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>

Related

How to track changes of a referenced element with React?

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

How to render html element in pre / REACT

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.

How to inject data in a html string in React?

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.

Adjacent JSX elements must be wrapped in an enclosing tag while adding bootstrap to my react code

Line 15:1: Parsing error: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>?
13 | //crossorigin></script>
14 |
> 15 | <script>var Alert = ReactBootstrap.Alert;</script>
| ^
Somewhere in your code you're doing:
const Example = () => (
<div />
<div />
)
You cannot do that. React can only returns one parent element. So you have to wrap it with another div:
const Example = () => (
<div>
<div />
<div />
</div>
)
However, this will bloat your markup with another div which is just to satisfy React. So you can use React.Fragment so that your markup doesn't have an unnecessary div like:
const Example = () => (
<React.Fragment>
<div />
<div />
</React.Fragment>
)
Or in short, you can write it as:
const Example = () => (
<>
<div />
<div />
</>
)

\n is not rendering the text in new line

I am having some dynamic JSON strings like below:
Status is unknown\nThe combination is excluded by the following restriction\nRestriction number 2. 01 Mar 12 08:03:01 0--Exclusion--OrderDeliveryTimeframe.equals(\"oneMonth\") && OrderShipping.equals(\"air\")\n\n
So when I am printing the same as output, \n is not rendering the text in new line. So I wrote the below code:
return <div>
{item.intro.replace(/[\n \n\n]/g, "\n")}
<br/>
Now the problem is - It is rendering the text in next line after encountering first occurrence of\n, but not after that. Neither it is working for \n\n. I think I am missing something. Can someone please help me with this. Thanks in advance.
\n isn't a newline in HTML, it's just a space. Any series of whitespace characters in HTML is treated as one space.
The React part of this is how you use br elements to do what you want to do.
The easiest way is to tell the div to treat whitespace different, as discussed in this question's answers.
return <div style={{whiteSpace: "pre-line"}}>
{item.intro}
</div>;
Or you could wrap the lines with an element, such as a div or p:
return <div>
{item.intro.split(/\n/).map(line => <div key={line}>{line}</div>)}
</div>;
Or if you want br elements between the lines, you can use fragments:
return <div>
{item.intro.split(/\n/).map(line => <React.Fragment key={line}>{line}<br/></React.Fragment>)}
</div>;
(We can't use the shorthand <>___</> form because they don't support keys.)
But I don't like that last one as it ends with a <br/>. You can fix that, but it's more complicated:
return <div>
{item.intro.split(/\n/).map((line, index) => index === 0 ? line : <React.Fragment key={line}>{line}<br/></React.Fragment>)}
</div>;
Probably one solution if you .split() the string by \n then using .map() rendering each element from the iteration between <p> tags.
Try as the following:
return <div>
{
item.intro.split('\n')
.map(e => <p>{e}</p>)
}
</div>
You would have similar result then - this is not using JSX, just for representation:
const text = 'random\ntext\nhello';
const div = document.getElementById('text');
const result = text.split('\n').map(e => `<p>${e}</p>`);
div.innerHTML = result.join('');
<div id="text"></div>
I hope this helps!
How about with only css white-space:pre :
You can run the below script, Hope that will help.
const { useState , useEffect } = React;
const App = () => {
const nstr = "Hi Vivek \nHow are you \n\nGlad to see you there";
return (
<div class='new-line'>
{ nstr }
</div>
);
}
ReactDOM.render(<App />, document.getElementById('react-root'));
.new-line {
white-space:pre
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react-root"></div>
Split the string using \n and then display them by looping the resulting array. This way the expected output will be obtained. Hope this helps
var text = "Status is unknown\nThe combination is excluded by the following restriction\nRestriction number 2. 01 Mar 12 08:03:01 0--Exclusion--OrderDeliveryTimeframe.equals(\"oneMonth\") && OrderShipping.equals(\"air\")\n\n
";
return (
<div>
{text.split("\n").map((t,key) => {
return <p key={key}>{t}</p>;
})}
</div>);

Categories