Append to existing JSX instead of using if-else conditional logic - reactjs

In my react classes, I often find myself having to use conditional logic to decide what to render. The problem with such an approach is that it leads to a lot of redundant markup. Here is an example:
if(this.props.quotes) {
return (
<div className="card">
<div className="item-1">{this.props.header}</div>
<div className="item-2">Add content...</div>
<i className="fa fa-quote-left"></i>
<i className="fa fa-quote-right"></i>
</div>
);
}
else {
return (
<div className="card">
<div className="item-1">{this.props.header}</div>
<div className="item-2">Add content...</div>
</div>
);
}
The only difference between the two HTML components is that one has two extra font-awesome elements. Ideally, you would want to use some base markup, and append content to it based on the result of the conditional.
I tried the following approach where I put the HTML content into an array and pushed the extra HTML elements in if the condition this.props.quotes was met:
var cardContent = [
<div className="item-1">{this.props.header}</div>,
<div className="item-2">Add content...</div>
];
if(this.props.quotes) {
cardContent.push(<i className="fa fa-quote-left"></i>);
cardContent.push(<i className="fa fa-quote-right"></i>);
}
return (
<div className="card">
{cardContent}
</div>
);
This introduces a new problem, mainly that React complains about missing key props in the array:
Warning: Each child in an array or iterator should have a unique "key" prop.
In this context, it doesn't make sense to give the elements keys since 3/4 of the content is static (the only non-static element is {this.props.header}).
Is there a better way of appending to existing JSX than the method I outlined above? I don't want to suppress all unique key prop warnings since it is still valid in the case of mapping. Is it better to just accept the redundant HTML approach?

When you render an Array of JSX elements, each one must have a key property on them. As you construct your array, you can do something like
cardContent.push(<div className="item-1" key="item-1">..</div>)
cardContent.push(<div className="item-2" key="item-2">..</div>)
I also want to mention for the example you've described, your elements are simple enough that having an inline condition rather than having two blocks that you conditionally return
return (
<div className="card">
<div className="item-1">{this.props.header}</div>
<div className="item-2">Add content...</div>
{this.props.quotes && <i className="fa fa-quote-left"></i>}
{this.props.quotes && <i className="fa fa-quote-right"></i>}
</div>
)

Related

How do I return components in React x amount of times?

I want to return <Component/> multiple times, in this for loop:
var i = 0;i < products.length;i++
It should return <Component /> five times because products.length = 5.
When I try returning this in my for loop it only returns one Component.
My code is
here.
The array, products, is defined here.
Thanks
You should add the 5 Product elements to a list rather than just returning one element in the for loop. If you use the return keyword inside the for loop, it will break the loop and stop executing afterwards.
E.g.
let result = []
For(let i = 0; i < products.length; i++) {
result.push((
<div className="col-md-3">
<div className="card">
<Link to={products[i].link}><img className="card-img-top" src={products[i].image} alt="Card cap" /></Link>
<div className="card-body">
<h5 className="card-title">
{products[i].name}
</h5>
<p className="card-text">
{products[i].description}
</p>
</div>
</div>
</div >
));
}
return result
You can then use the resulting array inside your render function in combination with the map function to display all the elements.
(I would rather put all the products in a normal JS array and just map over that inside your render function.)
If you want some additional reading, check out https://reactjs.org/docs/lists-and-keys.html
The reason your code does not work is on the first loop, it returns the entire component. It will never reach the second loop. You probably want to use map for your use case. Since map accepts a callback thats called for each item in the array, and you return the representation of the product for each entry from that callback (rather than returning in the actual component), you wont have the issue anymore.
const YourComponent = () => {
return products.map((product) => (
<div className="col-md-3">
<div className="card">
<Link to={product.link}><img className="card-img-top" src={product.image} alt="Card cap" /></Link>
<div className="card-body">
<h5 className="card-title">{product.name}</h5>
<p className="card-text">{product.description}</p>
</div>
</div>
</div>
))
}

react trying to set multiple values with an onclick button

When the user clicks a payment method card it should set the values of the usestates to the respective values.
How do I assign multiple values in the onClick button function? Thanks.
<div className="col-md-auto" key={p.id}>
<div className="card mb-4 shadow-sm">
<div className="card-body">
<p className="card-text"><b>{p.card_number}</b></p>
<p className="card-text"><b>{p.expiry_date}</b></p>
<p className="card-text"><b>{p.cardholder_name}</></p>
<p className="card-text"><b>{p.sort_code}</b></p>
<p className="card-text"><b>{p.cvv_number}</b></p>
<div className="btn-group mr-2">
<button
onClick={e => setExpiry_date(p.card_number),setExpiry_Date(p.expiry_date)}>
Click me!
</button>
</div>
</div>
</div>
</div>
I think having a handler might make it a bit more readable and you might be getting the errors because of the syntax and some missing commas.
I don't know if it's a mistake, but you are doing setExpiryDate twice in your handler for both card number and expiry date. I think there should be two different values. Will you mind sharing your whole component code for more clarity?
Also, try to use camelCase for all handers, some linters might not allow snake cases and React also uses camelCase itself.
Can you see if the following works for you.
const Component = () => {
const handleButtonClick = () => {
setCardNumber(cardNumber);
setExpiryDate(expiryDate);
}
return (
<button onClick={handleButtonClick}>
Click me
</button>
);
}

How to add conditional statements in reactJS map function

Am trying to render different tags based JSON keys, but it keeps throwing syntax error...
adding only one of the two conditions works though !
How can I do it right ?
<ul className="features">
{
property.details.features.map((idx, feature) =>
feature.hasOwnProperty(area) ?
<li>
<span className="icon-frame"/>
<div className="english">{feature[area]}m2 </div>
</li> : null
feature.hasOwnProperty(bedRoom) ? <li>
<span className="fa fa-bed"/>
<div className="english">{feature[bedRoom]}</div>
</li> :null
)
}
</ul>
The problem is you have an arrow function without braces in the body of the function, which means the expression right after the arrow will be what that function returns. But you have two expressions in a row there (the two ternary operators), so it won't know what to return, hence the syntax error. You can try this:
<ul className="features">
{
property.details.features.map((idx, feature) =>
feature.hasOwnProperty(area) ?
(
<li>
<span className="icon-frame"/>
<div className="english">{feature[area]}m2 </div>
</li>
)
: feature.hasOwnProperty(bedRoom) ?
(
<li>
<span className="fa fa-bed"/>
<div className="english">{feature[bedRoom]}</div>
</li>
)
: null
)
}
</ul>
So, if feature has the attribute area, then render the first, else, if it has the attribute bedrom, then render the second, else, return null. I tried to make it as readable as possible but it's never easy with long ternary operators.
You can always just wrap the body of the arrow function and do it with if, else and return statements.

Detect click of a div inside a container

Hi I am trying to detect when a div is clicked that is nested inside of a container.
the goal is to extract the src for an image as a variable.
I want the path to the image
Would this work ?
<div className = "container">
<div onClick{divClicked()} className="column">
<img alt="" src={props.gif} className="ui image" />
</div>
</div>
divClicked() {
console.log(props.gif)
}
You cannot set an onclick like that.
<div onClick={divClicked} className="column">
this code should work if the divClicked function is in the same context.
If this function is a member of a class, you might want to use this.divClicked
Additionally if you want to pass parameters to the function you can use arrow functions to do this.
<div onClick={()=>divClicked(someParam)} className="column">

In CSS, is ".class1.class2" legal and common usage?

I am quite used to seeing
div.class1
and
#someId.class1
but what about
.class1.class2
? And I think it is identical to
.class2.class1
? Because there was an element with id someId but now we have two elements of this type showing on the page, so I want to add a class and use the class instead of id, therefore the .class1.class2 instead of #someId.class1
It will select items with both classes. So not items with either one.
<span class="class1 class2"></span>
Yes, it is both legal and common. In the element, you would have something like this:
<div class="class1 class2">Hello</div>
It's nice for syntactic styling. To give you an example, let's say you have the following html:
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
You can add a second (and third, forth, etc.) class that modifies "box". For example:
<div class="first odd box">
</div>
<div class="second even box">
</div>
<div class="third odd box">
</div>
<div class="fourth even box">
</div>
Then, in styling, to style different box groups, you can do the following:
.odd.box {
}
.first.box, .fourth.box {
}
.first.box, .even.box {
}
This will be interpreted by the browser if you give your element does two class:
.class1.class2{width:500px;height:300px;}
<div class="class1 class2"> </div>
If you do like this, it will not be interpreted, resulting on a div with no styles:
.class1.class2{width:500px;height:300px;}
<div class="class2"> </div>
This will be interpreted (resulting on an element with a dimension of 500px X 300px:
.class1 {width:500px;}
.class2 {height:300px;}
<div class="class1 class2"> </div>
The common use of css, is to tell the browser that a certain element with and ID or CLASS of a certain name will get a set of styles, or tell the browser that a certain ID or CLASS will get a set of Styles, like so:
Ex 1:
.class1 {width:500px;} -> elements
with this class will get 500px of
width.
Ex 2:
div.class1 {width:500px;}
-> only a
div element with this class will get
500px of width.
Ex 3:
div.class1, h1.class1 {width:500px;}
-> only a div and a h1 element with this class will get 500px of width.
You can read valid information about css at:
W3C CSS SYNTAX PAGE
Just wanted to confirm the answer given by Jouke van der Maas,
which is the right answer. I would like to quote the following
excerpt from the CSS 2.1 specification:
5.2 Selector syntax
A simple selector is either a type selector or universal selector
followed immediately by zero or more attribute selectors, ID
selectors, or pseudo-classes, in any order. The simple selector
matches if all of its components match. [snip]
Since the .classname selector is equivalent to the [class="classname"] selector,
it is an attribute selector. Note the "in any order" bit. Hence the selector
.class1.class2
is identical to the selector
.class1.class2
and matches both elements like
<span class="class1 class2">Hello World</span>
as well as
<span class="class2 class1">Hello World</span>
which is the same thing, as well as
<span class="class1 class2 class3">Hello World</span>
etc...
You can also get even more fancy.

Resources