Set a multiline string as defaultValue in react-ace Ace Editor - javascript

When using the react-ace (Ace Editor) library, I find myself in the need to define the defaultValue property as a code block in the JSX format (the editor mode I have left in JavaScript), like this:
<div style={{height: "100%", width:"100%"}}>
<AceEditor
mode="javascript"
theme="github"
onChange={this.onChange}
name="UNIQUE_ID_OF_DIV"
defaultValue="export default function MyComponent({ children }) {..."
width="100%"
height="100%"
editorProps={{
$blockScrolling: true
}}
/>
</div>
When declaring defaultValue to get something like this:
export default function MyComponent({ children }) {
return (
<>
<header>
<a>
<Image
priority
className={utilStyles.borderCircle}
height={108}
width={108}
alt={name}
/>
</a>
<h2 className={utilStyles.headingLg}>
<a className={utilStyles.colorInherit}>{name}</a>
</h2>
</header>
<main>{children}</main>
{!home && (
<div className={styles.backToHome}>
<a>← Home</a>
</div>
)}
</>
)
}
The editor returns...
How can I get a multiline string into the editor as a default value?

You can use template literals to do the multiline statements.
In the official docs of AceEditor mentioned that you can pass a string for value attribute which should be reflected in the editor so when you pass a string inside " " it should not take new line so, when you use template literals ie,${``} we can give multiline sentences
so, its similar to how we pass a value in react to attributes
attribute-name = {"here we specify what should be the value"}
as in our case it should be a string which can be in multiple line
so attribute-name = {${``}}
Example:
value={`${`
//firstline
//secondline
...
//n line`}`}
reference :
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

Related

Why is an array that is passed correctly via props returning undefined?

I'm trying to load each string of an array of strings in a <li> html tag by passing this array via props:
<CardItem
src='https://static.news...koinex-banner.png'
text='my text'
label='Adventure'
path='/products'
description={["someText1", "someText2", "someText3", "someText4"]}
/>
function CardItem(props) {
return (
<>
<li className='cards__item'>
<Link className='cards__item__link' to={props.path}>
<figure className='cards__item__pic-wrap' data-category={props.label}>
<img
className='cards__item__img'
alt='Travel Image'
src={props.src}
/>
</figure>
<div className='cards__item__info'>
<h5 className='cards__item__text'>{props.text}</h5>
</div>
<CardDescription description={props.description} />
</Link>
</li>
</>
);
}
export default CardItem;
function CardDescription(props) {
return (
<div>
<ul>
<li>{props.description[0]} </li>
</ul>
</div>
)
}
export default CardDescription
And I'm getting
TypeError: Cannot read properties of undefined (reading '0')
I'm not sure why props.description prop is returning undefined.
Also, this TypeError seems to only be happening with the props.description prop.
Your code is misspelled CardDescrition to CardDescription
Try:
{props.description ? <CardDescription description={props.description} /> : ''}
and in description:
function CardDescription(props) {
return (
<div>
<ul>
{props.description.map(des => <li>des</li>)}
</ul>
</div>
)
}
please find the minimal repo I created:
https://github.com/snake-py/so-help-react-card
Explanation:
I try to explain from what I understand what is happening there.
When Carditems mounts it seems even though you hard code the values, that they are not passed on the initial render. Hence, the ternary check if the props include the description array.
I am guessing now why that is:
Perhaps because they are inside a wrapper component of Link. If you remove the Link component the code should work without the initial check.
well, that's probably because during the mounting of the three the description prop could be undefined, you could avoid this error by doing this props?.description[0], also if you want to render all the values in the array inside the CardDescrition component you could do this
props?.description.map((item) => (<li>{item}</li>))

Add <br> tag using replaceAll() in javascript

I have this string:
export default function App() {
const string = 'test data,cars,colors,demo';
return (
<div className="App">
<h1>Hello {string.replaceAll(',','<br>')}</h1>
</div>
);
}
I expect:
test data<br>cars<br>colors<br>demo
But i get one string without breack inside my string. How to achieve what i expect using replaceAll()?
In order to display html from string you have to use dangerouslySetInnerHTML
export default function App() {
const string = 'test data,cars,colors,demo';
return (
<div className="App">
<h1 dangerouslySetInnerHTML={{ __html: `Hello ${string.replaceAll(',','<br>')}`}}></h1>
</div>
);
}
Assuming this is a React application you need to use dangerouslySetInnerHTML to add the new HTML to the page.
function Example() {
const string = 'test data,cars,colors,demo';
const html = `Hello ${string.replaceAll(',', '<br>')}</h1>`;
return (
<div className="App">
<h1 dangerouslySetInnerHTML={{ __html: html }} />
</div>
);
}
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I don't suggest you dangerouslySetInnerHTML. It introduces both security and performance issues, and even if you are working on learning project, you better avoid such an approach.
The reason why your code does not work, JSX does not magically convert string "<br>" into appropriate element (until there is dangerouslySetInnerHTML, sure).
But JSX can render arrays just fine. It allows us to split initial string into elements: string.split(', ') and then inject JSX's <br /> with the help of .flatMap()(think about it as if .join() could return array with additional elements in between elements of source array).
{
string.
split(', ').
flatMap((el, index) => index ? [<br />, el]: el)
}
This approach is way more powerful than dangerouslySetInnerHTML since instead of simple BR you may use any JSX tree with React custom components, context and event handlers.
Another approach is to replace ', ' with newlines and apply CSS style white-space: pre-wrap(check white-space docs on all values available)

Template Literals - Changing the Inner HTML of an element (onInput event)

So I´ve never used Template Literals before, but I need to work with a template now that seemingly includes a form with Template Literals
This is one of my inputs:
<input
type="number"
className="mf-input "
min="1900"
max="2099"
step="1"
id="curyear"
name="currentyear"
placeholder="${ parent.decodeEntities(`Current Year`) } "
onInput=${parent.handleChange}
aria-invalid=${validation.errors['currentyear'] ? 'true' : 'false'}
ref=${el => parent.activateValidation({"message":"This field is required.","minLength":1900,"maxLength":2099,"type":"by_character_length","required":false,"expression":"null"}, el)}
/>
<${validation.ErrorMessage}
errors=${validation.errors}
name="currentyear"
as=${html`<span className="mf-error-message"></span>`}
/>
What I am trying to do is, in the onInput method, instead of handling the validation, I also want to change the innerHTML of an element:
<h2 class="elementor-heading-title elementor-size-default" id="curyeartext">Current Year</h2>
I´ve been trying to do it for hours, but I can't get it to work.
EDIT: turns out, they are template literals and not reactJS
Avoid setting innerHTML inside React, use state instead. This is because React will overwrite your modified DOM when it re-renders, if the html is in a node that React is controlling.
export default function MyReactComponent() {
var [ currentInput, setCurrentInput ] = React.useState();
return <>
<input
type="number"
className="mf-input"
min="1900"
max="2099"
step="1"
name="currentyear"
onInput={(e) => setCurrentInput(e.target.value)}
value={currentInput}
/>
<h2 class="elementor-heading-title elementor-size-default" id="curyeartext">
{currentInput}
</h2>
</>;
}
However, if you have a situation where it is unavoidable, you can tell React to ignore a node by only specifying a ref on it - i.e. no other props or child JSX:
export default function MyReactComponent() {
return <div ref={divRef => {
divRef.innerHTML = "Hello <b>world!</b>";
}} />
}
This technique is typically used when integrating non-React specific JS libraries into React, as you can do whatever you want with the contents of that DOM node.
I managed to do this by adding a function above the return:
function myfunction() {
document.querySelector('#curyeartext').innerHTML = document.querySelector('#curyear').value;
document.querySelector('#lastyear').innerHTML = document.querySelector('#curyear').value-1;
document.querySelector('#yearbefore').innerHTML = document.querySelector('#curyear').value-2;
}
And call it in the onInput event handler
onInput=${myfunction}

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.

Passing a number to a component

I am trying to pass a number to a React component but it is getting parsed as string (jsfiddle). How do I make React understand that I am passing a number? Should I cast the string to a number in the component?
var Rectangle = React.createClass({
render: function() {
return <div>
<div>{this.props.intValue + 10}</div>
<div>{this.props.stringValue + 10}</div>
</div>;
}
});
React.render(<Rectangle intValue="10" stringValue="Hello" />
, document.getElementById('container'));
<script src="https://facebook.github.io/react/js/jsfiddle-integration.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
It seems that in case of integers (and actually for strings as well) I should be passing the numbers via {} like so:
React.render(<Rectangle intValue={10} stringValue={"Hello"} />, document.getElementById('container'));
You can either use:
<MyComponent param1={10} param2={20} />
Or:
<MyComponent {...{param1: 10, param2: 20}} />

Categories