**I have a code that displays the news of the day. https://ibb.co/QMLY2Kx I have 10 classes named "block". Inside the "block" class there are two classes named "blockText". I need to get two different class names and not the same, I want to get this result "blockText1" and "blockText2". How to do it? **
import React from 'react';
import newsStyle from './News_module.css';
export class News extends React.Component {
render() {
const resultsRender = [];
for (var i = 0; i < this.props.news.length; i += 2) {
resultsRender.push(
<div class="block">
{
this.props.news.slice(i, i + 2).map((news, index) => {
return (
<div class="blockText" key={index}>
<p class="text">{news.title}</p>
{console.log(this.props.news.length)}
</div>
);
}
)
}
</div>
);
}
return (
<div>
<div className="headlineSecond">
<div className="Second">
{resultsRender}
</div>
</div>
</div>
);
}
}
You can use ternary operator for this . Here is an example where i chose the value of class based on the value of index and deciding upon whether it is even or odd
<div class={ index%2 ===0 ? "blockText1": "blockText2" } key={index}>
..... rest of code
</div>
I have a render function like this one:
render() {
const statement = true
return (
<div>
{
statement &&
<div>
<p>
{this.buildStuff()}
</p>
<p>
{this.buildStuff()}
</p>
<p>
{this.buildStuff()}
</p>
</div>
}
</div>
);
}
To avoid calling buildStuff() three times, I would like to assign it to a variable. How can I declare a variable after the line with statement &&?
A quick solution would be to do
const statement = true
const stuff = statement ? buildStuff() : null;
but this solution use two branches instead of one.
You can try this code on StackBlitz.
This what it would look like in Razor.
You can try something like this as well:
You can create a function that deals with this UI representation.
In this function, you can call buildStuff and have it return 3 <p> tags.
Then in main render, you can check your condition and render accordingly. This will make your render clean and declarative.
getBuildJSX() {
const stuff = this.buildStuff();
return Array.from({ length: 3}, () => <p> { stuff }</p>);
}
render() {
const statement = true
return (
<div>
{
statement ? this.getBuilsJSX() : null
}
</div>
);
}
Try it online
First solution (edit: alternative)
render() {
const statement = true;
const stuff = this.buildStuff(statement, 3); // jsx result looped in divs
return (
<div>
{
statement &&
<div>
{ stuff }
</div>
}
</div>
);
}
Alternative, memoization (caching of functions) if this is your goal:
const memoize = require('fast-memoize');
const memoized = memoize(this.buildStuff);
...
render() {
const statement = true;
return (
<div>
{
statement &&
<div>
<p>
{memoized()}
</p>
<p>
{memoized()}
</p>
<p>
{memoized()}
</p>
</div>
}
</div>
);
}
The true power of memoization however is, if you cache based on the parameter you give to buildStuff (maybe you move statement into buildstuff?). In your case I would just clean up the component and parameters in favour of readability rather than optimising something. So last alternative:
// Stuff is a component now
const Stuff = ({statement, stuff}) => {
if(!statement)
return null;
const result = stuff();
return (
<div>
<p>
{result}
</p>
<p>
{result}
</p>
<p>
{result}
</p>
</div>
)
}
render() {
return (
<div>
<Stuff statement={true} stuff={this.buildStuff} />
</div>
);
}
The benefit, you can now choose to pass the result or the function itself through props, and in the downward component either call the function or simply have its results passed through.
Single answer to your question in the headline: you cant, JSX is not a templating engine like Razor.
Explanation:
// JSX
<div id="foo">Hello world</div>
// Translates into
React.createElement("div", {id: "foo"}, "Hello world");
// JSX
<div>{ statement && <div /> }</div>
// Translates to
React.createElement("div", null, statement && React.createElement("div"));
Either you declare a new variable with an attribute, or you simply cant, since javascript does not allow variable creation inside parameters of functions.
I think one of the main ideas of react is to use components to structure your code.
So one way to do it would be like this:
render() {
const statement = true;
const Stuff = ({statement}) => {
if (!statement) { return null; }
return this.buildStuff();
}
return (
<div>
<p>
<Stuff statement={statement} />
</p>
<p>
<Stuff statement={statement} />
</p>
<p>
<Stuff statement={statement} />
</p>
</div>
);
}
Updated StackBlitz.
This answer is an answer to the problem but not a solution to the question. If you cannot declare a variable inside brackets in react (as you could do in Razor for example). Calling twice a statement can still be your best bet.
render() {
const statement = true
const stuff = statement ? this.buildStuff() : null
return (
<div>
{
statement &&
<div>
<p>
{stuff}
</p>
<p>
{stuff}
</p>
<p>
{stuff}
</p>
</div>
}
</div>
);
}
At least, we call this.buildStuff() only if needed and if we do, we call it only once.
I have seen similar questions here, but these haven't been helpful so far.
I have a component that has an array state:
eventData: []
Some logic watches for events and pushes the objects to the state array:
eventData.unshift(result.args);
this.setState({ eventData });;
unshift() here is used to push the new elements to the top of the array.
What I want to achieve is rendering the content of the state array. I have written a conditional that checks for a certain state, and based on that state decides what to output.
let allRecords;
if (this.state.allRecords) {
for (let i = 0; i < this.state.eventData.length; i++) {
(i => {
allRecords = (
<div className="event-result-table-container">
<div className="result-cell">
{this.state.eventData[i].paramOne}
</div>
<div className="result-cell">
{() => {
if (this.state.eventData[i].paramTwo) {
<span>Win</span>;
} else {
<span>Loose</span>;
}
}}
</div>
<div className="result-cell">
{this.state.eventData[i].paramThree.c[0]}
</div>
<div className="result-cell">
{this.state.eventData[i].paramFour.c[0]}
</div>
<div className="result-cell">
{this.state.eventData[i].paramFive.c[0] / 10000}
</div>
<div className="result-cell-last">
{this.state.eventData[i].paramSix.c[0]}
</div>
</div>
);
}).call(this, i);
}
} else if (!this.state.allRecords) {
for (let i = 0; i < this.state.eventData.length; i++) {
if (this.state.account === this.state.eventData[i].paramOne) {
(i => {
allRecords = (
<div className="event-result-table-container">
<div className="result-cell">
{this.state.eventData[i].paramOne}
</div>
<div className="result-cell">
{() => {
if (this.state.eventData[i].paramTwo) {
<span>Win</span>;
} else {
<span>Loose</span>;
}
}}
</div>
<div className="result-cell">
{this.state.eventData[i].paramThree.c[0]}
</div>
<div className="result-cell">
{this.state.eventData[i].paramFour.c[0]}
</div>
<div className="result-cell">
{this.state.eventData[i].paramFive.c[0] / 10000}
</div>
<div className="result-cell-last">
{this.state.eventData[i].paramSix.c[0]}
</div>
</div>
);
}).call(this, i);
}
}
}
Problems that I have with this piece of code:
The code always renders the very last value of eventData state object.
I would like to limit the rendered elements to always show not more than 20 objects (the last 20 records of the state array).
paramTwo is a bool, and according to its value I expect to see either Win or Loose, but the field is empty (I get the bool value via the console.log, so I know the value is there)
Is this even the most effective way of achieving the needed? I was also thinking of mapping through the elements, but decided to stick with a for loop instead.
I would appreciate your help with this.
A few things :
First, as the comments above already pointed out, changing state without using setState goes against the way React works, the simplest solution to fix this would be to do the following :
this.setState(prevState => ({
eventData: [...prevState.eventData, result.args]
}));
The problem with your code here. Is that the arrow function was never called :
{() => {
if (this.state.eventData[i].paramTwo) {
<span>Win</span>;
} else {
<span>Loose</span>;
}
}
}
This function can be reduced to the following (after applying the deconstructing seen in the below code) :
<span>{paramTwo ? 'Win' : 'Lose'}</span>
Next up, removing repetitions in your function by mapping it. By setting conditions at the right place and using ternaries, you can reduce your code to the following and directly include it the the JSX part of your render function :
render(){
return(
<div> //Could also be a fragment or anything
{this.state.allRecords || this.state.account === this.state.eventData[i].paramOne &&
this.state.eventData.map(({ paramOne, paramTwo, paramThree, paramFour, paramFive, paramSix }, i) =>
<div className="event-result-table-container" key={i}> //Don't forget the key like I just did before editing
<div className="result-cell">
{paramOne}
</div>
<div className="result-cell">
<span>{paramTwo ? 'Win' : 'Lose'}</span>
</div>
<div className="result-cell">
{paramThree.c[0]}
</div>
<div className="result-cell">
{paramFour.c[0]}
</div>
<div className="result-cell">
{paramFive.c[0] / 10000}
</div>
<div className="result-cell-last">
{paramSix.c[0]}
</div>
</div>
)
}
</div>
)
}
Finally, to only get the 20 first elements of your array, use slice :
this.state.eventData.slice(0, 20).map(/* CODE ABOVE */)
EDIT :
Sorry, I made a mistake when understanding the condition you used in your rendering, here is the fixed version of the beginning of the code :
{this.state.allRecords &&
this.state.eventData.filter(data => this.state.account === data.paramOne).slice(0, 20).map(/* CODE ABOVE */)
Here, we are using filter to only use your array elements respecting a given condition.
EDIT 2 :
I just made another mistake, hopefully the last one. This should ahve the correct logic :
this.state.eventData.filter(data => this.state.allRecords || this.state.account === data.paramOne).slice(0, 20).map(/* CODE ABOVE */)
If this.state.allRecords is defined, it takes everything, and if not, it checks your condition.
I cleaned up and refactored your code a bit. I wrote a common function for the repetitive logic and passing the looped object to the common function to render it.
Use Map instead of forloops. You really need to check this this.state.account === this.state.eventObj.paramOne statement. This could be the reason why you see only one item on screen.
Please share some dummy data and the logic behind unshift part(never do it directly on state object), we'll fix it.
getRecord = (eventObj) => (
<React.Fragment>
<div className="result-cell">
{eventObj.paramOne}
</div>
<div className="result-cell">
{eventObj.paramTwo ? <span>Win</span> : <span>Loose</span>}
</div>
<div className="result-cell">
{eventObj.paramThree.c[0]}
</div>
<div className="result-cell">
{eventObj.paramFour.c[0]}
</div>
<div className="result-cell">
{eventObj.paramFive.c[0] / 10000}
</div>
<div className="result-cell-last">
{eventObj.paramSix.c[0]}
</div>
</React.Fragment>
)
render() {
let allRecords;
if (this.state.allRecords) {
allRecords = <div>{this.state.eventData.map(eventObj => this.getRecord(eventObj)}</div>;
} else if (!this.state.allRecords) {
allRecords = <div>{this.state.eventData.map(eventObj => {
if (this.state.account === this.state.eventObj.paramOne) {
return this.getRecord(eventObj);
}
return null;
})}</div>;
}
return (<div className="event-result-table-container">{allRecords}</div>);
}
In my react render function, I am using array.map to return some JSX code in an array and then rendering it. This doesn't seem to work, I read a few questions here that suggested using return statement inside if/else block but that won't work in my case. I want to check whether round and duration are set on each array element and only then pass it in the JSX code.Could someone tell me a different approach please.
render() {
var interviewProcessMapped = interviewProcess.map((item, index) => {
return
{item.round ?
<div className="row title">
<div className="__section--left"><span className="section-title">Round {index + 1}</span></div>
<div className="__section--right">
<h3>{item.round}</h3>
</div>
</div>
: null
}
{
item.durationHours > 0 || item.durationMinutes > 0 ?
<div className="row">
<div className="__section--left">Duration</div>
<div className="__section--right border">
{item.durationHours > 0 ? <span>{item.durationHours} Hours</span> : null} {item.durationMinutes > 0 ? <span>{item.durationMinutes} Minutes</span> : null}
</div>
</div>
: null
}
});
return <div>{interviewProcessMapped}</div>
}
{ not required here:
return {item.round ?
If you use it that means you are returning an object.
Another issue is alone return means return; (automatic semicolon insertion) so either put the condition in same line or use ().
Write it like this:
render() {
let a, b;
var interviewProcessMapped = interviewProcess.map((item, index) => {
a = item.round ?
<div className="row title">
....
</div>
: null;
b = (item.durationHours > 0 || item.durationMinutes > 0) ?
<div className="row">
....
</div>
: null;
if(!a || !b)
return a || b;
return [a, b];
});
return (....)
}
You should probably use a combination of Array.prototype.map() and Array.prototype.filter()
No need for if at all
Here the pseudo code:
interviewProcess.filter(() => {
// return only if round and duration set
}).map(() => {
// transform the filtered list
});
I want to conditionally show and hide this button group depending on what is passed in from the parent component which looks like this:
<TopicNav showBulkActions={this.__hasMultipleSelected} />
__hasMultipleSelected: function() {
return false; //return true or false depending on data
}
var TopicNav = React.createClass({
render: function() {
return (
<div className="row">
<div className="col-lg-6">
<div className="btn-group pull-right {this.props.showBulkActions ? 'show' : 'hidden'}">
<button type="button" className="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
Bulk Actions <span className="caret"></span>
</button>
<ul className="dropdown-menu" role="menu">
<li>Merge into New Session</li>
<li>Add to Existing Session</li>
<li className="divider"></li>
<li>Delete</li>
</ul>
</div>
</div>
</div>
);
}
});
Nothing is happening however, with the {this.props.showBulkActions ? 'show' : 'hidden'}. Am I doing anything wrong here?
The curly braces are inside the string, so it is being evaluated as string. They need to be outside, so this should work:
<div className={"btn-group pull-right " + (this.props.showBulkActions ? 'show' : 'hidden')}>
Note the space after "pull-right". You don't want to accidentally provide the class "pull-rightshow" instead of "pull-right show". Also the parentheses needs to be there.
As others have commented, classnames utility is the currently recommended approach to handle conditional CSS class names in ReactJs.
In your case, the solution will look like:
var btnGroupClasses = classNames(
'btn-group',
'pull-right',
{
'show': this.props.showBulkActions,
'hidden': !this.props.showBulkActions
}
);
...
<div className={btnGroupClasses}>...</div>
As a side note, I would suggest you to try to avoid using both show and hidden classes, so the code could be simpler. Most likely, you don't need to set a class for something to be shown by default.
2021 addendum: for performance improvement, you can look into clsx as an alternative.
If you are using a transpiler (such as Babel or Traceur) you can use the new ES6 "template strings".
Here is the answer of #spitfire109, modified accordingly:
<div className={`btn-group pull-right ${this.props.showBulkActions ? 'shown' : 'hidden'}`}>
This approach allows you to do neat things like that, rendering either s-is-shown or s-is-hidden:
<div className={`s-${this.props.showBulkActions ? 'is-shown' : 'is-hidden'}`}>
you can simply do the following for example.
let classNameDependsOnCondtion = i18n.language == 'en' ? "classname" : "";
className={`flex flex-col lg:flex-row list-none ${classNameDependsOnCondtion }`}
OR
className={`flex flex-col lg:flex-row list-none ${i18n.language == 'en' ? "classname" : ""}`}
You can use here String literals
const Angle = ({show}) => {
const angle = `fa ${show ? 'fa-angle-down' : 'fa-angle-right'}`;
return <i className={angle} />
}
In case you will need only one optional class name:
<div className={"btn-group pull-right " + (this.props.showBulkActions ? "show" : "")}>
Replace:
<div className="btn-group pull-right {this.props.showBulkActions ? 'show' : 'hidden'}">`
with:
<div className={`btn-group pull-right ${this.props.showBulkActions ? 'show' : 'hidden'}`}
Or use npm classnames. It is very easy and useful especially for constructing the list of classes
Expending on #spitfire109's fine answer, one could do something like this:
rootClassNames() {
let names = ['my-default-class'];
if (this.props.disabled) names.push('text-muted', 'other-class');
return names.join(' ');
}
and then within the render function:
<div className={this.rootClassNames()}></div>
keeps the jsx short
2019:
React is lake a lot of utilities. But you don't need any npm package for that. just create somewhere the function classnames and call it when you need it;
function classnames(obj){
return Object.entries(obj).filter( e => e[1] ).map( e=>e[0] ).join(' ');
}
or
function classnames(obj){
return Object.entries(obj).map( ([cls,enb]) => enb? cls: '' ).join(' ');
}
example
stateClass= {
foo:true,
bar:false,
pony:2
}
classnames(stateClass) // return 'foo pony'
<div className="foo bar {classnames(stateClass)}"> some content </div>
Just For Inspiration
declaring helper DOM element and using it native toggle method:
(DOMTokenList)classList.toggle(class,condition)
example:
const classes = document.createElement('span').classList;
function classstate(obj){
for( let n in obj) classes.toggle(n,obj[n]);
return classes;
}
You can use ES6 arrays instead of classnames.
The answer is based on Dr. Axel Rauschmayer article: Conditionally adding entries inside Array and object literals.
<div className={[
"classAlwaysPresent",
...Array.from(condition && ["classIfTrue"])
].join(" ")} />
More elegant solution, which is better for maintenance and readability:
const classNames = ['js-btn-connect'];
if (isSelected) { classNames.push('is-selected'); }
<Element className={classNames.join(' ')}/>
simply use this approach--
<div className={`${this.props.showActions ? 'shown' : 'hidden'}`}>
this is much more neat and clean.
<div className={['foo', condition && 'bar'].filter(Boolean).join(' ')} />
.filter(Boolean) removes "falsey" values from the array. Since class names must be strings, anything other than that would not be included in the new filtered array.
console.log( ['foo', true && 'bar'].filter(Boolean).join(' ') )
console.log( ['foo', false && 'bar'].filter(Boolean).join(' ') )
Above written as a function:
const cx = (...list) => list.filter(Boolean).join(' ')
// usage:
<div className={cx('foo', condition && 'bar')} />
var cx = (...list) => list.filter(Boolean).join(' ')
console.log( cx('foo', 1 && 'bar', 1 && 'baz') )
console.log( cx('foo', 0 && 'bar', 1 && 'baz') )
console.log( cx('foo', 0 && 'bar', 0 && 'baz') )
you can use this:
<div className={"btn-group pull-right" + (this.props.showBulkActions ? ' show' : ' hidden')}>
This is useful when you have more than one class to append. You can join all classes in array with a space.
const visibility = this.props.showBulkActions ? "show" : ""
<div className={["btn-group pull-right", visibility].join(' ')}>
This would work for you
var TopicNav = React.createClass({
render: function() {
let _myClasses = `btn-group pull-right {this.props.showBulkActions?'show':'hidden'}`;
return (
...
<div className={_myClasses}>
...
</div>
);
}
});
Reference to #split fire answer, we can update it with template literals, which is more readable,For reference Checkout javascript template literal
<div className={`btn-group pull-right ${this.props.showBulkActions ? 'show' : 'hidden'}`}>
I have tried to tailored my answer to include all the best possible solution in the post.
There are many different ways of getting this done.
1. Inline inside the class
<div className={`... ${this.props.showBulkActions ? 'show' : 'hidden'}`}>
...
</div>
2. Using the values
var btnClass = classNames(
...
{
'show': this.props.showBulkActions,
'hidden': !this.props.showBulkActions
}
);
3. Using a variable
let dependentClass = this.props.showBulkActions ? 'show' : 'hidden';
className={`... ${dependentClass }`}
4. Using clsx
<div className={clsx('...',`${this.props.showBulkActions ? 'show' : 'hidden'}`)}>
...
</div>
You can use this npm package. It handles everything and has options for static and dynamic classes based on a variable or a function.
// Support for string arguments
getClassNames('class1', 'class2');
// support for Object
getClassNames({class1: true, class2 : false});
// support for all type of data
getClassNames('class1', 'class2', null, undefined, 3, ['class3', 'class4'], {
class5 : function() { return false; },
class6 : function() { return true; }
});
<div className={getClassNames('show', {class1: true, class2 : false})} /> // "show class1"
Based on the value of this.props.showBulkActions you can switch classes dynamically as follows.
<div ...{...this.props.showBulkActions
? { className: 'btn-group pull-right show' }
: { className: 'btn-group pull-right hidden' }}>
I would like to add that you can also use a variable content as a part of the class
<img src={src} alt="Avatar" className={"img-" + messages[key].sender} />
The context is a chat between a bot and a user, and the styles change depending of the sender, this is the browser result:
<img src="http://imageurl" alt="Avatar" class="img-bot">
A function to return the correct class based on a param (if present)
getClass(param){
let podClass = 'classA'
switch(param.toLowerCase()){
case 'B':
podClass = 'classB'
break;
case 'C':
podClass = 'classC'
break;
}
return podClass
}
Now just invoke this function from the div where the corresponding class is to be applied.
<div className={anyOtherClass + this.getClass(param)}
I successfully used this logic to apply the correct color to my bootstrap table rows.
<div className={"h-3 w-3 rounded-full my-auto " + (index.endDate ==="present"? "bg-green-500":"bg-red-500")}></div>
Don't Forget to add an extra space after the static class names.