There have been many questions on StackOverflow relating to applying conditional classnames to React components; however, I have not seen a good answer for this particular situation:
I have a basic div that I want to conditionally apply the "is-required" class. Here is my approach:
<div className={`some-class ${isRequired && 'is-required'}`}>
The main issue here is that when isRequired is false, then my compiled HTML code ends up looking like this:
<div class='some-class false'>
Obviously, I could use a ternary operator like this so I can return an empty string instead:
<div className={`some-class ${isRequired ? 'is-required' : ''}`}>
But then in the compiled HTML code there is this extra random space included in the class, which won't cause any rendering issues, but I still don't like it:
<div class='some-class '>
Even still, I could remove the space after "someClass" and include it before "isRequired", but now it's harder to read and feels kind of clunky:
<div className={`some-class${isRequired ? ' is-required' : ''}`}>
I have heard of utilities such as classnames, but I am looking for a simple solution where I don't need any additional packages.
What is the recommended approach here?
Actually, there are many ways to do that, here's one of them.
<div className={isRequired ? 'some-class is-required': 'some-class'}>
or you can return null
<div className={isRequired ? 'is-required' : null}>
In order, if you have several classes.
<div className={isRequired ? 'some-class is-required': isDisabled ? 'some-disabled-class' : 'some-class'}>
https://reactjs.org/docs/conditional-rendering.html
class App extends React.Component {
constructor() {
super();
this.state = {
isRequired: false
};
}
render() {
return (
<div className="App">
<div className={this.state.isRequired ? 'is-required' : null}>Null</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='root'></div>
Maybe you'll find this utility function helpful (it will be used as a tagged template):
const c = (strings = [], ...classes) => {
let myClass = '';
strings.forEach((s, i) => {
myClass += s + (classes[i] || '');
});
return myClass.trim().replace(' ', ' ');
}
Now you can use it like this :
className={c`my-class ${this.props.done && 'done'} selected`}
or
className={c`some-class ${isRequired && 'is-required'} ${isDisabled && 'some-disabled-class'}`}
You can use it if you don't want false value.
<div className={`some-class${isRequired && ' is-required' || ''}`}>
<div className={isRequired && 'is-required'}>Required</div>
Using evaluation short circuiting (https://en.wikipedia.org/wiki/Short-circuit_evaluation)
I highly recommend using this package for joining class names if you are using an import/export system.
https://www.npmjs.com/package/classnames
Related
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)
I would appreciate any help in getting this sorted out. In the code below I would like to take the values between `` from the span and assign them to a variable in order to have the following logic:
${research.data.codesRelated[1].code} === undefined
? ${research.data.description}
: ${research.data.codesRelated[1].code} ${research.data.description}
How can I do this? It seems so easy but I couldn't sort it out, don't know where to place the const & conditional rendering inside the code and to make it work. Everyone in advance.
The code is below:
const Research: FC<ResearchProps> = memo(
({ research, hideLabel = false, intl }) => (
<div className="section research">
<div className="section__value" id="container-research-value">
<div className="research-info">
<div className="description" id="element-research-description">
<PopoverWrapper
id="element-research-description-tooltip"
trigger="hover"
placement="top"
speaker={<span className="text-tooltip">{research.data.description}</span>}
>
**<span>{`${research.data.codesRelated[1].code} ${research.data.description}`}</span>**
</PopoverWrapper>
</div>
You can try this
const Research: FC<ResearchProps> = memo(
({ research, hideLabel = false, intl }) => (
<div className="section research">
<div className="section__value" id="container-research-value">
<div className="research-info">
<div className="description" id="element-research-description">
<PopoverWrapper
id="element-research-description-tooltip"
trigger="hover"
placement="top"
speaker={<span className="text-tooltip">{research.data.description}</span>}
>
<span>{research.data.codesRelated[1].code === undefined ?
research.data.description
: <>
{research.data.codesRelated[1].code} {research.data.description}
</>}
</span>
</PopoverWrapper>
</div>
As already was mentioned, you can use the ternary operator.
You can use a simple hack: conditional rendering for code with space at the end of the string template.
<span>
{research.data.codesRelated[1].code && `${research.data.codesRelated[1].code} `}
{`${research.data.description}`}
</span>
But the best way to create an external function for build this string. And your render will be like this:
<span>
{buildResearchDescription(research.data)}
</span>
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>
I need to show one element only if location is right, if not, it shoudn't be shown.
This is not working:
<div *ngIf="!accessTrue() && window.location.href.indexOf('something')" > -1)>
CODE
</div>
you cannot access the window object inside the template.
But you can define a getter in your component :
get hasSomething(){
return window.location.href.indexOf('something') > -1
}
then :
<div *ngIf="!accessTrue() && hasSomething">
CODE
</div>
Note that it might be cleaner to use an ActivatedRoute if your parameter is accessible through it.
In HTML :
<div *ngIf="hasAccess"> CODE </div>
In angular Component :
constructor(){
this.hasAccess = window.location.href.indexOf('something') > -1 && !this.accessTrue();
}
i have read some of pug documentation. its said that i have to install pug first and i'm already done that. then i have to require pug in my js file.
but i don't know where to write the compile for pug file in my react files? what is the right steps to use pug in react framework?
thanks! i really appreciated any help.
here is one of my component in react that i would like to render it with pug.
import React from 'react';
import Sidebar from './Sidebar';
import Header from './header/Header';
import {tokenverify} from '../../utils/helpers';
import pug from 'pug';
class Home extends React.Component {
componentDidMount() {
const token = localStorage.getItem('token')
tokenverify(token)
.catch((res) => {
this.props.history.push('/')
})
}
render() {
return(
<div className="main-container">
<div className="col-md-1">
<Sidebar history={this.props.history} username={this.props.params.username}/>
</div>
<div className="col-md-11">
<div className="row">
<Header history={this.props.history} username={this.props.params.username} />
</div>
<div className="row">
{this.props.children}
</div>
</div>
</div>
)
}
}
export default Home
I found this project in very early phase of its developmentĀ : https://github.com/bluewings/pug-as-jsx-loader.
I like it because it lets me write my dumb (presentational) react components as pug templates.
The only JSX functionality it currently supports are iterating and conditional if. Which seems good enough for writing most of the dumb components.
Here are the steps to use it
1. Install pug-as-jsx-loader
npm install pug-as-jsx-loader --save-dev
For next step you will have to eject if you are using create-react-app
2. Tell webpack how to handle pug templates.
In your webpack.config.dev.js,
{ test: /\.pug$/, use: [require.resolve('babel-loader'), require.resolve('pug-as-jsx-loader')] },
3. Import pug template in your component
import myTemplate from './mycomponent.pug'
4. Return compiled template from render function
const MyComponent = ({someProperty, someOtherProperty})=> {
return myTemplate.call({}, {
someProperty,
someOtherProperty
});
};
5. Define a pug to render component
#my-element
ul.my-list
li(key='{something.id}', #repeat='something as someProperty')
div(className='planet') {something.name}
div(className='vehicle') {something.type}
div(className='overview') {something.cost}
div(className='cancel', onClick='{()=> someOtherProperty(something)}')
div(className='no-mobile fa fa-remove')
A read about my experience : https://medium.com/p/7610967954a
With Pug, you have two options: render template to HTML string, passing the data object right away or render template to an efficient javascript function that outputs html when passed a data object.
When using pug(alone) with dynamic data, the choice is obviously to compile to function, so that data can be applied on the client.
However, React does not actually consume, or send to the client, html.
If you read an explanation of JSX, you will see that it is just HTML-lookalike syntactic sugar that gets compiled to a javascript function that programmatically creates DOM nodes (essential for the way React handles diffing and updating the page). Pug at the moment, even on the client, outputs an HTML string. Hence, the only way we will be able to use it is
dangerouslySetInnerHTML as following:
//from https://runkit.io/qm3ster/58a9039e0ef2940014a4425b/branches/master?name=test&pug=div%20Wow%3A%20%23%7Ba%7D%23%7Bb%7D
function pug_escape(e){var a=""+e,t=pug_match_html.exec(a);if(!t)return e;var r,c,n,s="";for(r=t.index,c=0;r<a.length;r++){switch(a.charCodeAt(r)){case 34:n=""";break;case 38:n="&";break;case 60:n="<";break;case 62:n=">";break;default:continue}c!==r&&(s+=a.substring(c,r)),c=r+1,s+=n}return c!==r?s+a.substring(c,r):s}
var pug_match_html=/["&<>]/;
function pug_rethrow(n,e,r,t){if(!(n instanceof Error))throw n;if(!("undefined"==typeof window&&e||t))throw n.message+=" on line "+r,n;try{t=t||require("fs").readFileSync(e,"utf8")}catch(e){pug_rethrow(n,null,r)}var i=3,a=t.split("\n"),o=Math.max(r-i,0),h=Math.min(a.length,r+i),i=a.slice(o,h).map(function(n,e){var t=e+o+1;return(t==r?" > ":" ")+t+"| "+n}).join("\n");throw n.path=e,n.message=(e||"Pug")+":"+r+"\n"+i+"\n\n"+n.message,n}function test(locals) {var pug_html = "", pug_mixins = {}, pug_interp;var pug_debug_filename, pug_debug_line;try {;var locals_for_with = (locals || {});(function (a, b) {;pug_debug_line = 1;
pug_html = pug_html + "\u003Cdiv\u003E";
;pug_debug_line = 1;
pug_html = pug_html + "Wow: ";
;pug_debug_line = 1;
pug_html = pug_html + (pug_escape(null == (pug_interp = a) ? "" : pug_interp));
;pug_debug_line = 1;
pug_html = pug_html + (pug_escape(null == (pug_interp = b) ? "" : pug_interp)) + "\u003C\u002Fdiv\u003E";}.call(this,"a" in locals_for_with?locals_for_with.a:typeof a!=="undefined"?a:undefined,"b" in locals_for_with?locals_for_with.b:typeof b!=="undefined"?b:undefined));} catch (err) {pug_rethrow(err, pug_debug_filename, pug_debug_line);};return pug_html;}
// pug source: "div Wow: #{a}#{b}"
// this would obviously be much shorter if you include pug-runtime globally in your application
function createMarkup(a,b) {
return {__html: test({a:a,b:b})};
}
function MyComponent(props) {
return <div dangerouslySetInnerHTML={createMarkup(props.a, props.b)}/>;
}
ReactDOM.render(
<MyComponent a="banana" b="&patata"/>,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id=root />
Alternatively, there are attempts to translate jade or pug syntax into react directly, such as pug-react-compiler and babel-plugin-transform-pug-to-react. It seems they solved including further react components inside the pug template, which might be a desirable tradeoff for them possibly having quirks.