Dynamically made strings for id in JSX - javascript

I want to make id like this dynamically.
<div id="track_1"></div>
<div id="track_2"></div>
So I gave the id like this from parent component.
export default function Components(props) {
return (
<AudioTrack trackNum="1"/>
<AudioTrack trackNum="2"/>
)
}
then in my AudioTrack Component I got the trackNum and want to use like this
const AudioTrack = (props) => {
return(
<div id="track_{props.trackNum}" ></div>
);
}
Howeber it doesn't work.
Is there any good way?

Since the div prop isn't a constant string, you need {} to indicate an expression, and then either use + to concatenate or a template literal with ${}:
<div id={`track_${props.trackNum}`}></div>

Related

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)

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.

How to inject a dinamically created element into an existing div in React JSX?

I have a list of objects photos, from a json data file, that I would like to organize into 3 different <div> columns, but I dont know how to achieve that, here is my broken non-optimized code:
<div className="container">
<div ref={leftColRef} className="left-col" />
<div ref={centreColRef} className="centre-col" />
<div ref={rightColRef} className="right-col" />
{Object.keys(photos).forEach((n, i) => {
const id = photos[n].id;
const thumb = photos[n].thumbnailUrl;
const title = photos[n].title;
const element = (
<Thumbnail id={id} title={title} thumb={thumb} />
);
if (i % 3 === 0) {
leftColRef.current.append(element);
} else if (i % 3 === 1) {
centreColRef.current.append(element);
} else {
rightColRef.current.append(element);
}
// this line works, it idsplays the data but is commented as the data needs to go inside its respective columns
// return <Thumbnail key={id} title={title} thumb={thumb} />;
})}
</div>
The idea is to insert some elements into the left-column when i%3 = 0 and others in the centre-column when i%3 = 1 and so on ...
And a link to my codesandbox
Any help/advise will be much appreciated.
Easiest is probably to prepare the data outside the render function and to render the column one by one.
You should not manipulate the DOM like it's done in jQuery using JSX
Example:
const Component = (props) => {
const filterPhotos = (column) => {
return props.photos.filter((photo,index)=> index%3==column);
}
return <>
<MyColumn photos={filterPhotos(0)}/>
<MyColumn photos={filterPhotos(1)}/>
<MyColumn photos={filterPhotos(2)}/>
</>;
}
First, using ref on div to inject stuff on it is wrong. It's the opposite of how react works.
Like charlies said, I would split the photos in 3 different arrays before the render. Then, you'll be able to do something like this :
<div ref={leftColRef} className="left-col" />
{ photosLeft.map(photo => <Thumbnail key={photo.id} {...photo} />)
</div>
when preparing your data, try to use the same object properties and component props name so you can spread it easily ( {...photo} ).
Note: Also, when rendering an array in react, each child must have a unique key props. It will help react to render on that part of dom if your data change.

How to convert a string into React element- ReactJs/ Javascript

I crated a react html element like:
let elements = (
<div>
<div>dwewe</div>
<div>wefwef</div>
<span>yurhfjer</span>
</div>
);
and now I wanted to pass this to an html attribute, hence I converted the react element into string using:
<span data-tip-react={ReactDOMServer.renderToString(element)}>{title}></span>
I'm now able to access these elements, however I'd like to convert it back to react element (the way it was before conversion)
here is what I'm expecting the o/p as:
I tried using DOMParser, however it returned an html element that React did not accept for rendering and threw an errr: not a react element
How do I convert the string back into the same format - React element??
please help!
thanks
Following here :
dynamic HTML String to react component
You can use dangerouslySetInnerHTML (for simple element) or some npm package :
class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
getDom() {
return (
<div>
<div>dwewe</div>
<div>wefwef</div>
<span>yurhfjer</span>
</div>
);
}
convertToString(dom) {
console.log("To String", ReactDOMServer.renderToString(dom))
return ReactDOMServer.renderToString(dom)
}
convertToDOM(string) {
let domparser = new DOMParser();
console.log("To Dom", domparser.parseFromString(string, 'text/html'))
return domparser.parseFromString(string, 'text/html')​​
}
render() {
return (
<div>
<Hello name={this.state.name} />
<p>
Start editing to see some magic happen :)
{<div dangerouslySetInnerHTML={{__html: this.convertToString(this.getDom())}}></div>}
</p>
</div>
);
}
}
ex : https://stackblitz.com/edit/react-mhsqfd

Declaring function inside class component vs inside functional component

I am playing with function syntax inside and outside class components. Can anyone explain to me why the print function works when written like this
const UploadButton = (props)=> {
const fileName = 'myfile';
props.getFileName(fileName)
function print(){console.log('onClick worked')}
return(
<div>
<input onClick= {print()} type="file" id = {fileName}/>
</div>
)
}
but when i write it like i would when declaring it inside a class component:
print(){console.log('onClick worked')}
i get this error
Line 10: Parsing error: Unexpected token, expected ";"
8 | props.getFileName(fileName)
9 |
> 10 | print(){console.log('onClick worked')}
| ^
This behavior is not tied with React but fundamentally is a method vs. function thing in JavaScript.
When you declare functions with some context it becomes a method. So, In a class setup, the functions are actually methods.
In Javascript, it is possible to declare a function within another function, that is why this works
const UploadButton = (props)=> {
const fileName = 'myfile';
props.getFileName(fileName)
function print(){console.log('onClick worked')}
return(
<div>
<input onClick= {print()} type="file" id = {fileName}/>
</div>
)
}
But when you don't specify the function keyword and the declaration is not inside of class it throws error.
print(){console.log('onClick worked')}
Parsing error: Unexpected token, expected ";"
If you rather used an arrow function here print=()=>{console.log('onClick worked')}, it would work because its a function expression and is treated as a normal variable declaration scoped to the enclosing function.
print(){console.log('onClick worked')}
I think when you write this in a functional component the compiler does not know that you are trying to define a function and it is rather trying to execute the print function, hence it is expecting a ';'
However, In class based components when you define a function using the above syntax, when the class is converted to a function, the print method will be added to its prototype.
One issue that you're having with your functional component is that you're calling the print function, then passing whatever it returns, which is undefined in this case, to the onClick handler of your input element.
Your JSX for the input element should look like this:
const UploadButton = (props)=> {
// ...
return (
<div>
<input onClick={print} type="file" id={fileName}/>
</div>
)
}
When dealing with class components, however, your UploadButton component, should look like the following:
class UploadButton extends React.Component {
print() {
console.log('onClick worked')
}
render() {
// ...
this.props.getFileName(fileName)
return (
<div>
<input onClick={this.print} type="file" id = {fileName}/>
</div>
)
}
}
Also, you probably shouldn't be using an input element as your UploadButton. Just use a button element instead, something like the following example:
<form>
<div>
<label for="file">Choose file to upload</label>
<input type="file" id={fileName} />
</div>
<div>
<!--Should be something along the lines of `this.handleSubmit`
rather than `this.print`, but you get the idea-->
<button onClick={this.print}>Submit</button>
</div>
</form>

Categories