How to avoid rendering element as simple as possible - javascript

I have a reactJS component that looks like this :
var LikeCon = React.createClass({
render(){
return this.renderLikeButton(this.props.like, this.props.likeCount)
},
renderLikeButton(like, likeCount){
var content;
var tmpLikeCount;
if(likeCount < 1){
tmpLikeCount = "";
}
else{
tmpLikeCount = likeCount;
}
if(like == 1){
content = <div className="likeButConAct"><div className="likeB"> </div><div className="likeCount">{tmpLikeCount}</div></div>
}
else{
content = <div className="likeButCon"><div className="likeB"> </div><div className="likeCount">{tmpLikeCount}</div></div>
}
return content;
}
});
Say that I want to hide the likeCount element if there is no likes. How do I do this as simple as possible? I donĀ“t want another component to render this.

If your variable is null or undefined then React simply won't render it. That means your conditional code can be as simple as:
var tmpLikeCount;
if(likeCount >= 1){
tmpLikeCount = likeCount;
}
But I think you can make your code even simpler using class sets:
http://facebook.github.io/react/docs/class-name-manipulation.html
var LikeCon = React.createClass({
render(){
var likeCountCmp;
var classes = React.addons.classSet({
likeButCon: true,
active: this.props.like
});
if(this.props.likeCount > 0) {
likeCountCmp = <div className="likeCount">{this.props.likeCount}</div>;
}
return (
<div className={classes}>
<div className="likeB"> </div>
{likeCountCmp}
</div>
)
}
});
A final variation that I think will work is to use an implicit function return:
var LikeCon = React.createClass({
render(){
var classes = React.addons.classSet({
likeButCon: true,
active: this.props.like
});
return (
<div className={classes}>
<div className="likeB"> </div>
{this.getLikeCountCmp()}
</div>
)
},
getLikeCountCmp: function() {
if(this.props.likeCount > 0) {
return <div className="likeCount">{this.props.likeCount}</div>;
}
}
});
if we don't specifically return anything from getLikeCountCmp, we end up with undefined, which React renders as nothing.
Note that I'm a bit confused with your like == 1 comparison - should that be true/false rather than a number? I've assumed this.props.like will be true or false in my examples. That means it'd be called with:
<LikeCon like={true|false} likeCount={5} />

If you like to put everything inline, you can do this:
renderLikeButton(like, likeCount){
return (<div className={like==1 ? "likeButConAct" : "likeButCon" }>
<div className="likeB"> </div>
{ likeCount > 0 ? <div className="likeCount">{likeCount}</div>: null }
</div>);
}
That way you wont be rendering .likeCount div if likeCount is 0.
jsFiddle: http://jsfiddle.net/715u9uvb/

what about using the className to hide the element?
something like :
var cssClasses = "likeButConAct ";
if ( likeCount < 1 ) {
cssClasses += "hidden";
}
...
return <div className=cssClasses><div ...
EDIT
var content;
var tmpLikeCount;
var likeCounterComponent;
if(likeCount > 0){
likeCounterComponent = <div className="likeCount">{likeCount}</div>
}
if(like == 1){
cssClasses = "likeButConAct"
}
else{
cssClasses = "likeButCon";
}
return (
<div className=cssClasses>
<div className="likeB"> </div>
{ likeCounterComponent }
</div>);
You can add the likeCounter only if there are likes. If there are likes the likeCounterComponent contains the JSX code to render the likes counter, otherwise is undefined and therefore nothing will be rendered.
I haven't tried to run the code, but I guess you got the idea to solve this problem. :D

Colin's answer looks good to me.. if your issue is with having aspects of rendering extracted to a separate function, you don't HAVE to do that. This works too:
return (
<div className={classes}>
<div className="likeB"> </div>
{this.props.likeCount > 0 && (
<div className="likeCount">{this.props.likeCount}</div>
)};
</div>
)

....
if (likeCount < 1) {
return "";
}
....

Related

Passing data between components and use the ngDocheck

We are trying to pass data from one component to another and below is the approach we are taking. When there is no data we want to show the error message and if there is data we show that in the select box.
showGlobalError = true;
constructor(
private psService: ProjectShipmentService,
private pdComp: ProjectDetailsComponent
) {
this.psService.tDate.subscribe(x => this.cachedResults = x);
}
ngOnInit() { }
ngDoCheck() {
if (this.cachedResults.length > 0 && this.count <= 1) {
this.showGlobalError = false;
this.populateArrays();
this.count++;
}
}
populateArrays() {
this.reportingProject = [this.pdComp.rProjectNumber];
this.projectSalesOrder = this.pdComp.rSalesOrder;
this.clearFilter();
// ........
Our HTML looks like below:
<div *ngIf="showGlobalError">
<h6>The reporting project doesn't have any Shippable Items</h6>
</div>
<div id="search-block" class="box-shadow-block">
<span>Reporting Project</span>
<dx-select-box
[items]="reportingProject"
[text]="reportingProject"
[readOnly]="true"
>
</dx-select-box>
</div>
The issue is The Reporting Project number appears in the select box but when I click on the select box and click anywhere else in the page the project number disappears. I am not sure if this has something to do with the ngDoCheck(). Any help is greatly appreciated
set first your showGlobalError to false and do the logic in your ngOnInit.
showGlobalError = false;
in your view:
<div *ngIf="showGlobalError">
<h6>The reporting project doesn't have any Shippable Items</h6>
</div>
<div *ngIf="!showGlobalError" id="search-block" class="box-shadow-block">
<span>Reporting Project</span>
<dx-select-box
[items]="reportingProject"
[text]="reportingProject"
[readOnly]="true"
>
</dx-select-box>
</div>
use following code to get data from service.
this.psService.tDate.subscribe(x => {this.cachedResults = x},
(err) => {},
() => {this.checkForCachedResults()}
);
and write function to execute your conditional check instead of doCheck() like follows
checkForCachedResults() {
if (this.cachedResults.length > 0 && this.count <= 1) {
this.showGlobalError = false;
this.populateArrays();
this.count++;
}
}

onClick event returning Every Modal instead of a Single Modal

I'm trying to build a Jeopardy like game using React and Redux. I currently have an onClick event set to each li, but whenever I click on it, I get every Modal to pop up instead of the one that is attached to that li item. I have my code separated in different files but I believe these two files are the only ones I need to show:
const ModalView = React.createClass({
pass: function(){
console.log('pass clicked');
store.dispatch({type:"MODAL_TOGGLE"})
},
submit: function(){
console.log('submit clicked');
store.dispatch({type:"MODAL_TOGGLE"})
},
render: function(){
let question = this.props.question
let category = this.props.category
let answer = this.props.answer
let val = this.props.value
return (
<div>
<div className="modal">
<p>{category}</p>
<p>{question}</p>
<p>{answer}</p>
<p>{val}</p>
<input
type="text"
placeholder="type in your answer">
</input>
<button onClick={this.submit}>Submit</button>
<button onClick={this.pass}>Pass</button>
</div>
</div>
)
}
})
and ValuesView
const ValuesView = React.createClass({
modalPopUp: function(value){
store.dispatch({type:"MODAL_TOGGLE"})
},
render: function(){
let showClass = "show-content"
let hideClass = "hide-content"
if (this.props.modal){
showClass = "hide-content"
hideClass = "show-content"
}
return (<div>
<ul className="list">
{this.props.datum.clues.slice(0,5).map((data, i) => {
if (data.value === null){
return <div>
<div className={hideClass}>
<ModalView
category = {this.props.category}
question = {data.question}
answer = {data.answer}
value ={data.value} />
</div>
<li onClick={this.modalPopUp} key={i}>$600</li>
</div>
}
return <div>
<div className={hideClass}>
<ModalView
category = {this.props.category}
question = {data.question}
answer = {data.answer}
value ={data.value}/>
</div>
<li
category = {this.props.category}
onClick={this.modalPopUp} key={i}>${data.value}</li>
</div>
})}
</ul>
</div>
)
}
})
How would I go about only getting the corresponding Modal to display instead of every one? Thanks!!
If you just want to code real Modal I suggest you to use some already implemented component like https://github.com/reactjs/react-modal (I'm not saying is mandatory, nor even whit the example I suggest, could be other)
I think that store.dispatch({type:"MODAL_TOGGLE"}) in some way toggle modal prop between true and false. Then you just use that flag to toggle a class to show or hide content I guess (I would need to see you css).
The problem with this approach is (apart that is not the best way to do this for many reasons) that you are using the same class for every item in your clues array.
In some way you need to store which is the "modal" you want to show, and then in the render, just apply the show class to this item.
Maybe:
const ValuesView = React.createClass({
modalPopUp: function(index){
return function (event) {
store.dispatch({
type:"MODAL_TOGGLE",
payload: {
modalIndex: index // Save modalIndex prop
}
})
}
},
render: function(){
return (<div>
<ul className="list">
{this.props.datum.clues.slice(0,5).map((data, i) => {
if (data.value === null){
return <div>
<div className={(this.prosp.modal && this.props.modalIndex === i) ? "show-content" : "hide-content"}>
<ModalView
category = {this.props.category}
question = {data.question}
answer = {data.answer}
value ={data.value} />
</div>
<li onClick={this.modalPopUp(i)} key={i}>$600</li>
</div>
}
return <div>
<div className={(this.prosp.modal && this.props.modalIndex === i) ? "show-content" : "hide-content"}>
<ModalView
category = {this.props.category}
question = {data.question}
answer = {data.answer}
value ={data.value}/>
</div>
<li
category = {this.props.category}
onClick={this.modalPopUp(i)} key={i}>${data.value}</li>
</div>
})}
</ul>
</div>
)
}
})

Reusing properties within map loop in ReactJS - best practice

I am trying to figure out the best way to render two blocks of code in ReactJS, one will be used for desktop and the other for mobile. Functionality wise they will do exactly the same thing but have different markup wrapped around them, an example would be a carousel that renders differently on mobile.
I am using the map function to iterate over the object properties, I have working code below but I am duplicating variables and reassigning the same values which is obviously inefficient as I am doing this for each code block.
Can anyone suggest a nice / best practice way of doing what I need?
Sorry for the basic question!
render: function() {
return (
<div>
<div className="hidden-xs">
{
this.state.userItems.map(function (item) {
var someValue = 'value' in item ? item.value : '';
var anotherValue = 'anotherValue' in item ? item.anotherValue : '';
return (
<div key={someValue}>
{someValue}<br>{anotherValue}
</div>
)
})
}
</div>
<div className="visible-xs">
{
this.state.userItems.map(function (item) {
var someValue = 'value' in item ? item.value : '';
var anotherValue = 'anotherValue' in item ? item.anotherValue : '';
return (
<div key={someValue}>
<div className="differentMarkup">
{someValue}<br>{anotherValue}
</div>
</div>
)
})
}
</div>
</div>
)}
Updated answer:
If the inner items can have different markup, I would say it depends how different their markup is going to be. For example, you could have two separate methods that prepare the markup of the mobile and non-mobile version of the items separately. Something like this:
renderUserItem: function(itemData) {
return (
<div key={itemData.someValue}>
{itemData.someValue}<br>{itemData.anotherValue}
</div>
)
},
renderMobileUserItem: function(itemData) {
return (
<div key={itemData.someValue}>
<div className="differentMarkup">
{itemData.someValue}<br>{itemData.anotherValue}
</div>
</div>
)
},
prepareItemData: function(item) {
return {
someValue: item.value ? item.value : '';
anotherValue: item.anotherValue ? item.anotherValue : '';
};
},
render: function() {
// Prepare the variables you will need only once
let parsedUserItems = this.state.userItems.map(this.prepareItemData);
return (
<div>
<div className="hidden-xs">
{ parsedUserItems.map(this.renderUserItem) }
</div>
<div className="visible-xs">
{ parsedUserItems.map(this.renderMobileUserItem) }
</div>
</div>
)
}
You could also have a single method if the differences between mobile and non-mobile are not too big. For example, using ES6 arrow functions:
renderUserItem: function(itemData, renderWrapper) {
return (
<div key={itemData.someValue}>
{renderWrapper ? <div className="differentMarkup"> : ''}
{itemData.someValue}<br>{itemData.anotherValue}
{renderWrapper ? </div> : ''}
</div>
)
},
render: function() {
// Prepare the variables you will need only once
let parsedUserItems = this.state.userItems.map(this.prepareItemData);
return (
<div>
<div className="hidden-xs">
{ parsedUserItems.map(item => this.renderUserItem(item, false)) }
</div>
<div className="visible-xs">
{ parsedUserItems.map(item => this.renderUserItem(item, true)) }
</div>
</div>
)
}
Original answer below:
If I understood correctly and the inner items have the same markup, you could extract the function passed into map to a separate method:
renderUserItem: function(item) {
var someValue = 'value' in item ? item.value : '';
var anotherValue = 'anotherValue' in item ? item.anotherValue : '';
return (
<div key={someValue}>
{someValue}<br>{anotherValue}
</div>
)
},
render: function() {
return (
<div>
<div className="hidden-xs">
{ this.state.userItems.map(this.renderUserItem) }
</div>
<div className="visible-xs">
{ this.state.userItems.map(this.renderUserItem) }
</div>
</div>
)
}

jQuery match part of class with hasClass

I have several div's with "project[0-9]" classes:
<div class="project1"></div>
<div class="project2"></div>
<div class="project3"></div>
<div class="project4"></div>
I want to check if the element has a "project[0-9]" class. I have .hasClass("project") but I'm stuck with matching numbers.
Any idea?
You can use the startswith CSS3 selector to get those divs:
$('div[class^="project"]')
To check one particular element, you'd use .is(), not hasClass:
$el.is('[class^="project"]')
For using the exact /project\d/ regex, you can check out jQuery selector regular expressions or use
/(^|\s)project\d(\s|$)/.test($el.attr("class"))
A better approach for your html would be:
I believe these div's share some common properties.
<div class="project type1"></div>
<div class="project type2"></div>
<div class="project type3"></div>
<div class="project type4"></div>
Then you can find them using:
$('.project')
$('div[class*="project"]')
will not fail with something like this:
<div class="some-other-class project1"></div>
$('div[class^="project"]')
will fail with something like this:
<div class="some-other-class project1"></div>
Here is an alternative which extends jQuery:
// Select elements by testing each value of each element's attribute `attr` for `pattern`.
jQuery.fn.hasAttrLike = function(attr, pattern) {
pattern = new RegExp(pattern)
return this.filter(function(idx) {
var elAttr = $(this).attr(attr);
if(!elAttr) return false;
var values = elAttr.split(/\s/);
var hasAttrLike = false;
$.each(values, function(idx, value) {
if(pattern.test(value)) {
hasAttrLike = true;
return false;
}
return true;
});
return hasAttrLike;
});
};
jQuery('div').hasAttrLike('class', 'project[0-9]')
original from sandinmyjoints: https://github.com/sandinmyjoints/jquery-has-attr-like/blob/master/jquery.hasAttrLike.js
(but it had errrors so I fixed it)
You can improve the existing hasClass method:
// This is all that you need:
(orig => {
jQuery.fn.hasClass = function(className) {
return className instanceof RegExp
? this.attr('class') && this.attr('class')
.split(/\s+/)
.findIndex(name => className.test(name)) >= 0
: orig.call(this, className);
}
})(jQuery.fn.hasClass);
// Test the new method:
Boolean.prototype.toString = function(){ this === true ? 'true' : 'false' };
const el = $('#test');
el.append("hasClass('some-name-27822'): " + el.hasClass('some-name-27822'));
el.append("\nhasClass(/some-name-\d+/): " + el.hasClass(/some-name-\d+/));
el.append("\nhasClass('anothercoolclass'): " + el.hasClass('anothercoolclass'));
el.append("\nhasClass(/anothercoolclass/i): " + el.hasClass(/anothercoolclass/i));
el.append("\nhasClass(/^-name-/): " + el.hasClass(/^-name-/));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<pre id="test" class="some-name-0 some-name-27822 another-some-name-111 AnotherCoolClass"></pre>
why don't you use for to check numbers
for (var i = 0; i < 10; i++) {
if(....hasClass("project"+i))
{
//do what you need
}
}

How can I select the "shallowest" matching descendant?

How can I select the first "shallowest" input?
My current selection will be the div marked "selected".
I won't know how many levels down it will be.
<div class="selected"> <!-- already have this -->
<div class="unknown-number-of-wrapper-panels">
...
<div class="collection">
<div class="child">
<input type="text" value="2" /> <!-- don't want this -->
</div>
</div>
<input type="text" value="2" /> <!-- need this -->
<input type="text" value="2" />
...
</div>
</div>
It seems like find().first() gives me the deepest one.
Edited for clarity. I need to find it based on the fact that it is shallower, not based on other unique attributes.
This might be like a reverse of closest() ?
If I understand your issue, you need to recursively check the child nodes for elements with that class.
function findShallowest( root, sel ) {
var children = root.children();
if( children.length ) {
var matching = children.filter( sel );
if( matching.length ) {
return matching.first();
} else {
return findShallowest( children, sel );
}
} else {
return null;
}
}
var selected = $('.selected');
findShallowest( selected, ':text' );
Example: http://jsfiddle.net/Qf2GM/
EDIT: Had forgotten a return statement, and had an ID selector instead of a class selector for the initial .selected.
Or make it into your own custom plugin:
Example: http://jsfiddle.net/qX94u/
(function($) {
$.fn.findShallowest = function( sel) {
return findShallowest( this, sel );
};
function findShallowest(root, sel) {
var children = root.children();
if (children.length) {
var matching = children.filter(sel);
if (matching.length) {
return matching.first();
} else {
return findShallowest(children, sel);
}
} else {
return $();
}
}
})(jQuery);
var result = $('.selected').findShallowest( ':text' );
alert( result.val() );
You are after a breadth-first search rather than the depth-first search (which jQuery's find() uses). A quick google has found: http://plugins.jquery.com/project/closestChild
This could be used like this:
$(...).closestChild('input')
Just to golf this "plugin" a bit - Uses #user113716's technique, just reduced code size.
$.fn.findShallowest = function( selector ) {
var children = this.children(),
matching = children.filter( selector );
// return an empty set if there are no more children
if ( !children.length ) {
return children;
}
// if anything matches, return the first.
if ( matching.length ) {
return matching.first();
}
// check grand-children
return children.findShallowest( selector );
};
Try on jsFiddle
This is another approach. The idea is that you get the matching element with the least number of ancestors:
(function($) {
$.fn.nearest = function(selector) {
var $result = $();
this.each(function() {
var min = null,
mins = {};
$(this).find(selector).each(function() {
var n_parents = $(this).parents().length,
if(!mins[n_parents]) {
mins[n_parents] = this;
min = (min === null || n_parents < min) ? n_parents : min;
}
});
$result = $result.add(mins[min]);
});
return $result;
};
}(jQuery));
Usage:
$('selected').nearest('input');
DEMO
findShallowest, as #patrick has it, might be a better method name ;)
If you don't know the class name of the bottom level element you can always use something like
$('.unknown-number-of-wrapper-panels').children().last();
Well given your markup, would the following work?
$('.selected div > input:first')

Categories

Resources