React keeps escaping the amp character (&) in attributes - javascript

I have a component with a data-icon attribute. The value of this attribute should be, for instance, &#xf00f so that css can render it via content: attr( data-icon );.
However, whatever I try: React keeps escaping to &. Even when I provide the proper unicode character \u0026#xf00f.
Is there some way to stop React from messing with that value? Besides dangerously setting inner html, as I do not want to add another wrapper.
Component
define( [ 'react', 'util' ], function( React, Util )
{
return React.createClass(
{
render: function()
{
//var amp = '\u0026',
var amp = String.fromCharCode( 38 ),
// Util.icons[x] returns a String, such as "f00f"
code = amp + '#x' + Util.icons[this.props.name] + ';';
return (
<i data-icon={code}>
{this.props.children ? <span>{this.props.children}</span> : null}
</i>
);
}
} );
} );
Usage
<Widget.Icon name="add" />
Output
<i data-icon="&#xf0fb;" data-reactid=".170lse36465.7.0"></i>

Well, I just realized, that for my particular use case I can simply get away with:
<i data-icon={String.fromCharCode( "f00f" )} />
https://github.com/facebook/react/issues/3769

Related

React dangerouslySetInnerHTML Inside Fragment

I have a use-case where I need to format some text in React and also render HTML.
Here is an example of what I'm currently trying to achieve:
import React, {Fragment} from "react";
import {renderToString} from "react-dom/server";
function FormatText(props) {
const lines = props.children.split('\n');
return lines.map((line, index) => (
<Fragment key={index}>
{line}{index !== lines.length - 1 && <br/>}
</Fragment>
));
}
const content = {
first: 'This is some text\nwith new\n\nline characters - 1',
second: 'This is some text\nwith new\n\nline <strong>characters - <sup>2</sup></strong>',
};
function App() {
return (
<ol>
<li>
<FormatText>{content.first}</FormatText>
</li>
<li dangerouslySetInnerHTML={{
__html: renderToString(<FormatText>{content.second}</FormatText>)
}}/>
</ol>
)
}
As you can see, I have some content which contains \n characters and HTML. Calling the renderToString function converts the HTML into encoded characters, which means the HTML is not rendered properly.
Is there a way to render HTML inside a react fragment.
Ideally I wanted to do the following (but it doesn't):
function FormatText(props) {
const lines = props.children.split('\n');
return lines.map((line, index) => (
<Fragment key={index} dangerouslySetInnerHTML={{__html: renderToString(
<Fragment>
{line}{index !== lines.length - 1 && <br/>}
</Fragment>
)}}>
</Fragment>
));
}
<Fragment> doesn't adds any node to DOM and so you can't do dangerouslySetInnerHTML on it. It is basically a functionality provided by React to avoid addition of extra node to DOM when you needed to return more than one from return in render. So, if something doesn't exists on real DOM, you can't do anything on it.
renderToString is generally used on node server. When doing server side rendering, you want to send the html from server to client. So, better avoid renderToString also.
The issue is that, html doesn't recognises \n for new line etc. It needs html tags. The approach to use FormatText is fine or you can simply convert the \n to br and then use dangerouslySetInnerHTML inside the <li> tag.
const content = {
first: 'This is some text\nwith new\n\nline characters - 1',
second: 'This is some text\nwith new\n\nline <strong>characters - <sup>2</sup></strong>',
};
function App() {
return (
<ol>
<li dangerouslySetInnerHTML={{
__html: content.first.replace(/\n/g, "<br/>")
}}/>
<li dangerouslySetInnerHTML={{
__html: content.second.replace(/\n/g, "<br/>")
}}/>
</ol>
)
}
ReactDOM.render(<App/>, document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Hope it helps. Revert for any doubts.
Hi I guess it is not possible, only way hot to pass formated html into DOm is via dom element DIV.
Maybe this link could help you or point to
https://reactjs.org/docs/dom-elements.html

React : how to prevent escaping in data-attribute

I have a JSON that looks like this { "id":"xyz", "height":1024, "width":1024 } which I would like to have in a data attribute like :
<div data-command='{"id":"xyz","height":1024,"width":1024}'></div>
but when I use react it escapes the string as shown below :
<div data-command='{"id":"xyz","height":1024,"width":1024}'></div>
I use this code to generate the element
React.createElement("div",
{ "data-command" : JSON.stringify({ "id":"xyz", "height":1024, "width":1024 }), null)
does anyone know how I can get the JSON without the " escaping?
If it's not possible how can I transform it back in javascript so I can use JSON.parse after?
The 'dangerouslySetInnerHTML' attribute is precisely used for scenarios like this.
createMarkup() {
return {__html: `<div data-command='{"id":"xyz","height":1024,"width":1024}'></div>`};
}
render() {
return (
<div dangerouslySetInnerHTML={this.createMarkup()}>
</div>
);
}

Have both JSX and normal className together

I have a css style class that needs to be in JSX form as it depends on the state, but I also need to use style class the ordinary way for styling. How do I combine these together?
I've tried
<span className={statusColor} className="uppercase">
Which only takes into account the last class one
<span className={statusColor uppercase}>
Which obviously looks for a variable called uppercase and errors out
<span className="uppercase {statusColor}">
Neither of them work
Below is a dummied-down version of my code. What is the correct way to do this?
const Component = React.createClass({
return {
completed: false
}
},
render () {
let statusColor;
this.state.completed === true ? statusColor = "green" : statusColor = "red";
return (
<div className="status-banner">
<span className="kata-text-status">Status: <span className={statusColor} className="uppercase">{this.state.completed.toString()}</span></span>
</div>
)
}
})
Try this:
<span className={`uppercase ${statusColor}`}>
This will use an ES6 template string (`uppercase ${statusColor}`) and interpolate statusColor with ${...}.
<span className={'uppercase' + ' ' + statusColor}>
You can do it in several ways.
Firstly you can use + to concat more than one string variables and generate dynamic strings
<span className={"uppercase " + statusColor}>
Or you can use npm modules classnames.
The brackets {} translate to normal JS. So you can just use + to concatenate.
<span className={"uppercase " + statusColor}>

REACT JS : Uncaught Error: parse Error: Unexpected token

I am new to react (I am incorporating it into my ruby on rails project), here the component ive made:
<div id="articles"></div>
<script type="text/jsx">
var Article=React.createClass({
render: function(){
return(
<div>
{this.props.data}.map(function(item){
{item.name}
})
</div>
)
}
});
var input = [{name: "hello", email: "hello#example.com"}]
React.render(<Article data={input} />, document.getElementById("articles"))
When i run this, this is the error i get:
Uncaught Error: Parse Error: Line 7: Unexpected token .
{item.name}
I have set a prop to be an array. I just want to output the name hash key inside the array - why am i getting this error, it seems to me that this should work ?
You're not returning anything inside your map-statement, try writing
return {item.name};
However, I would suggest you try moving your map-function outside of the immediate render, its easier to read and less error-prone.
<div id="articles"></div>
<script type="text/jsx">
var Article=React.createClass({
render: function() {
var rows = this.props.data.map(function(item) {
//You could return any valid jsx here, like <span>{item.name}</span>
return item.name;
});
return(
<div>
{rows}
</div>
)
}
});
var input = [{name: "hello", email: "hello#example.com"}
React.render(<Article data={input} />, document.getElementById("articles"))
In JSX you have two 'modes'. JS mode (default) and JSX mode.
You enter JSX mode with a tag, like <div. In JSX mode only tags, attributes, and arbitrary text are allowed. You are also allowed to go into a JS mode with {}. This can happen any number of times.
function jsMode(){
<jsx>
{js(
moreJs,
<moreJsx>{evenMoreJs}</moreJsx>
)}
</jsx>;
}
So coming back to your code:
<div>
{this.props.data}.map(function(item){
{item.name}
})
</div>
Let's break this down into chunks
// js mode
<div> // begin jsx
{ // begin js
this.props.data // some js code
} // end js, back to jsx
.map(function(item) // this is just plain text visible to the user
{ // begin js
{item.name} // some invalid js, SyntaxError
} // end js
) // this is just plain text visible to the user
</div> // end jsx
// js mode
Because you want the .map to be interpreted as JS, and you were previously in JSX mode, it should also be in the {}s.
<div>
{this.props.data.map(function(item){
{item.name}
})}
</div>
Also, inside the .map callback, you're still in JS mode, so you need to remove the {}s
<div>
{this.props.data.map(function(item){
item.name
})}
</div>
And finally, you need to return the name from the .map callback.
<div>
{this.props.data.map(function(item){
return item.name;
})}
</div>
Other stuff
The code above will work, but probably not as expected.
If data was [{name: 'foo'}, {name: 'bar'}], map will return ['foo', 'bar'], and react will render it as:
<span>foo</span><span>bar</span>
To the user this appears as "foobar". If you want it to appear as two separate words you could put a space after each name:
<div>
{this.props.data.map(function(item){
return item.name + " ";
})}
</div>
Or return an element from .map and style it as you like. For example, an unordered list of names. Note that here we wrap item.name in {}s because we enter JSX mode.
<ul>
{this.props.data.map(function(item, i){
return <li key={i}>{item.name}</li>;
})}
</ul>
We also provide a key. In this case it's the index of the item, which works as long as the list never changes, or only has items added/removed to/from the end of the array.
Image credit: wikipedia/commons
If the array is reordered, or items are added/remove to/from the start or middle, we need a unique string identifier for each item.
If your data was [{name: "foo", id: "14019751"}, {name: "bar", id: "13409185"}], then:
<ul>
{this.props.data.map(function(item, i){
return <li key={item.id}>{item.name}</li>;
})}
</ul>

Rendering "a" with optional href in React.js

I need to render a table with a link in one of the columns, and searching for a most elegant way to do it. My main problem is - not all table rows are supplied with that link. If link is present - I need that "a" tag rendered. If not - no need for "a" tag at all. Generally speaking I would like react to handle that choice (render vs not render) depending on this.state.
This is what I have at the moment.
React.createClass({
getInitialState: function () {
return {
pipeline: this.props.data.pipeline,
liveUrl: this.props.data.liveUrl,
posted: this.props.data.created,
expires: this.props.data.end
};
},
render: function () {
return (
<tr className="posting-list">
<td>{this.state.pipeline}</td>
<td>Posted</td>
<td>
<input className="datepicker" type="text" value={this.state.posted}/>
</td>
<td>
<input className="datepicker" type="text" value={this.state.expires}/>
</td>
<td>UPDATE, DELETE</td>
</tr>
);
}
});
This results is DOM element :
XING_batch
This is not acceptable solution for me, because those blank hrefs are still clickable.
I also tried adding some logic to getInitalState(
liveUrl: (this.props.data.liveUrl !== "") ? this.props.data.liveUrl : "javascript:void;",
), which worked fine, but looks weird, and adds errors in console(Uncaught SyntaxError: Unexpected token ;)
The only way I've got left is creating 2 different components for
It's just JavaScript, so you can use any logic you like, e.g.:
<td>
{this.state.liveUrl
? <a ...>{this.state.pipeline}</a>
: this.state.pipeline}
</td>
You can choose the type of component at runtime, as well:
import * as React from "react";
const FooBar = props => {
const Component = props.href ? "a" : "div";
return (
<Component href={href}>
{props.children}
</Component>
);
};
<FooBar>Hello</FooBar> // <div>Hello</div>
<FooBar href="/">World</FooBar> // World
Take a look at spread properties:
You could use them like this for example:
var extras = { };
if (this.state.liveUrl) { extras.href = this.state.liveUrl; }
return <a {...extras} >My link</a>;
The values are merged with directly set properties. If they're not on the object, they're excluded.

Categories

Resources