Show if any conditions are present - javascript

I have been going back to some javascript docs to try and figure this out. But, no luck. What I am trying to do is, basically if any of the data is present show this jsx block.
{speaker.email || speaker.room &&
<>
<h3>Contact</h3>
<ul className="list-unstyled">
<li><a href={"mailto:speaker.email"}>{speaker.email}</a></li>
<li><span>{speaker.room} {speaker.building}</span></li>
<li>{speaker.phone}</span></li>
</ul>
</>
}
Is there a way using js or react to have multiple conditions using 'or'?
This is probably a terrible example but something like this:
{speaker.email || speaker.room || speaker.building || speaker.phone &&

#jmargolisvt was right, you can simply wrap with parens.
{(speaker.email || speaker.room || speaker.building || speaker.phone) &&
<>
...
</>
}

Simply, Use if condition and return the JSX
if (speaker.email || speaker.room || speaker.building || speaker.phone) { // any number of condition can be joined by ||
return (
<>
<h3>Contact</h3>
<ul className="list-unstyled">
<li><a href={"mailto:speaker.email"}>{speaker.email}</a></li>
<li><span>{speaker.room} {speaker.building}</span></li>
<li>{speaker.phone}</span></li>
</ul>
</>
);
}

I tested it and it worked like following. temp link https://codesandbox.io/s/determined-dhawan-k3yms
{speaker.email ||
(speaker.room && (
<>
<h3>Contact</h3>
<ul className="list-unstyled">
<li>
<a href={"mailto:speaker.email"}>{speaker.email}</a>
</li>
<li>
<span>
{speaker.room} {speaker.building}
</span>
</li>
<li>
<a href={speaker.phone}>
<span className="font-weight-bold">{speaker.phone}</span>
</a>
</li>
</ul>
</>
))}

Related

How to change .jsx component style from inside the .js file? React

function Navbar() {
const [shownavcontents, setShownavcontents] = useState(false)
if(shownavcontents){
document.getElementsByClassName("navbardivofmobiledevice").style.display = "none";
}else{
document.getElementsByClassName("navbardivofmobiledevice").style.display = "block";
}
return (
<>
<div className="top">
<Searchbar />
<AiOutlineMenu size={20} className="outlinemenu" onClick={() => {setShownavcontents(true)}} />
</div>
<div className="navbardivofmobiledevice">
<ul>
<li>
Home
</li>
<li>
Members
</li>
<li>
All Posts
</li>
<li>
My Posts
</li>
</ul>
</div>
</>
);
}
As you see I am trying to make responsive navbar, in this case, for mobile devices. I've faced one problem. I've made button on top of navbar and some navbar contents which I want to display only whenever user will click this button and vice versa. So I tried using hooks to check if the user clicked the button which works perfectly, only thing that doesn't works is this if else statements it seems like document.getElementsByClassName("navbardivofmobiledevice").style.display = "none"; doesn't have an effect here. So my question is what is the alternative of this? What can I do here?
This is imperative code:
document.getElementsByClassName("navbardivofmobiledevice").style.display = "none";
With React, you rarely get references to DOM elements and update them manually, and in any case, you do it using Refs, not with the getElement... or querySelector... methods). Instead, you write declarative code and let React take care of the DOM updates for you.
In this case, simply add or remove a hidden attribute or CSS class that has display: none from your JSX:
function Navbar() {
const [shownavcontents, setShownavcontents] = useState(false);
return (
<>
<div className="top">
<Searchbar />
<AiOutlineMenu size={20} className="outlinemenu" onClick={() => {setShownavcontents(true)}} />
</div>
<div className="navbardivofmobiledevice" hidden={ !shownavcontents }>
<ul>
<li>
Home
</li>
<li>
Members
</li>
<li>
All Posts
</li>
<li>
My Posts
</li>
</ul>
</div>
</>
);
}
If you prefer to use a class, assuming you have defined a CSS class .isHidden { display: none; } you would use this line instead:
<div className={ `navbardivofmobiledevice${ shownavcontents ? '' : ' isHidden' }` }>
Regarding what some comments are mentioning about rendering that conditionally like so:
function Navbar() {
const [shownavcontents, setShownavcontents] = useState(false);
return (
<>
<div className="top">
<Searchbar />
<AiOutlineMenu size={20} className="outlinemenu" onClick={() => {setShownavcontents(true)}} />
</div>
{ shownavcontents && (
<div className="navbardivofmobiledevice">
<ul>
<li>
Home
</li>
<li>
Members
</li>
<li>
All Posts
</li>
<li>
My Posts
</li>
</ul>
</div>
) }
</>
);
}
I would avoid that, as hiding your main navigation from Google and other search engines will harm your SEO. You need to hide it visually but still have it in the DOM.
If you want to do better than that, add all the appropriate ARIA attributes and logic for a navigation menu with nested submenus, as explained here:
https://www.w3.org/WAI/ARIA/apg/example-index/menubar/menubar-navigation

Each child in a list should have a unique "key" prop while nested list iteration

I have this React code:
export default function Technologies({ technologies }) {
return (
<>
{ Object.keys(technologies).map((key, i) => (
<span>
<span className={styles.post}><h3 className={'bold300'}>{key}</h3></span>
<div className={styles.techList}>
<ul key={i}>
{ Object.keys(technologies[key]).map((key2, idx) => (
<li key={idx}>{key2}
{ technologies[key][key2].map(key3 => (
<span key={key3.name} className={styles.badge}><Image src={key3.name} width={key3.w} height={key3.h} /></span>
)) }
</li>
)) }
</ul>
</div>
</span>
)) }
</>
)
}
Here you can see nested iteration, and everywhere I use i or idx to create unique key for list, but still keep getting warning for this string:
...
<ul key={i}>
...
As I said, I know what this error means, and even know how to fix, but not in this case, I just don't know, where I should put key to prevent this warning. Thanks!
The key should be present on the root element. So, the first key should be on the span and not on the ul.
export default function Technologies({ technologies }) {
return (
<>
{Object.keys(technologies).map((key, i) => (
<span key={i}>
<span className={styles.post}>
<h3 className={"bold300"}>{key}</h3>
</span>
<div className={styles.techList}>
<ul>
{Object.keys(technologies[key]).map((key2, idx) => (
<li key={idx}>
{key2}
{technologies[key][key2].map((key3) => (
<span key={key3.name} className={styles.badge}>
<Image src={key3.name} width={key3.w} height={key3.h} />
</span>
))}
</li>
))}
</ul>
</div>
</span>
))}
</>
);
}

Output ul tags conditionally in JSX?

Setup: Headless Wordpress CMS, GatsbyJs, GraphQL
I think this is a JavaScript ES6 problem rather than a GatsbyJS problem.
I've got a query that outputs an author, title and url.
The data is queried from an ACF repeater field, so there could be one, none or many items.
Here's the code:
{citationPath &&
citationPath.map(item => {
return (
<li key={item.url}>
<a href={item.url} target="_blank" rel="noopener noreferrer">
{item.title}
</a>
<p>Author: {item.author}</p>
</li>
)
})}
THIS WORKS, no problem.
I now want to put a <ul> tag before the code, and a </ul> tag after the code ONLY if some citationPath items
are returned.
OPTION #1
I could do this:
{citationPath &&
citationPath.map(item => {
return (
<ul>
<li key={item.url}>
<a href={item.url} target="_blank" rel="noopener noreferrer">
{item.title}
</a>
<p>Author: {item.author}</p>
</li>
</ul>
)
})}
but then I get a <ul></ul> around each list item.
OPTION #2
I could do this:
<ul>
{citationPath &&
citationPath.map(item => {
return (
<li key={item.url}>
<a href={item.url} target="_blank" rel="noopener noreferrer">
{item.title}
</a>
<p>Author: {item.author}</p>
</li>
)
})}
</ul>
but then I get <ul></ul> when NO citationPath items are returned.
OPTION #3
When I try this:
{citationPath && parse("<ul>")}
{citationPath &&
citationPath.map(item => {
return (
<li key={item.url}>
<a href={item.url} target="_blank" rel="noopener noreferrer">
{item.title}
</a>
<p>Author: {item.author}</p>
</li>
)
})}
{citationPath && parse("</ul>")}
No HTML is printed out on those posts which do not return citationPath data. However, on those that do I get the following HTML output:
<ul></ul>
<li>
<a href=[my url] target="_blank" rel="noopener noreferrer">
<li>
[more list items ...]
So it seems that {citationPath && parse("<ul>")} and {citationPath && parse("</ul>")} are being executed before:
{citationPath &&
citationPath.map(item => {
return (
<li key={item.url}>
<a href={item.url} target="_blank" rel="noopener noreferrer">
{item.title}
</a>
<p>Author: {item.author}</p>
</li>
)
})}
Note: the reason I don't simply go for OPTION #2 is that I don't want screen readers bumping into empty <ul></ul> tags.
Does any one have any ideas on how to solve this?
You just need to put the condition above and make sure to check the condition of length.
{citationPath && citationPath.length > 0 && (
<ul>
{citationPath.map(item => {
return (
<li key={item.url}>
<a href={item.url} target="_blank" rel="noopener noreferrer">
{item.title}
</a>
<p>Author: {item.author}</p>
</li>
);
})}
</ul>
)}
Since this checks both the length and the truethy value of citationPath, this is the solution you're looking for and as an industry professional, I use this code in production.

Mapping array inside array with React

I have a database in MongoDB and one of the props in the document contains an array. I'm trying to map the collection in the client-side using React using this code:
<div>
<h2>Recipes</h2>
<ul>
{this.state.recipes.map(recipe =>
<li key={recipe._id}>
{recipe.title}<br/>{recipe.category}<br/>
{recipe.ingredients}<br/>{recipe.preparation}
</li>
)}
</ul>
</div>
ingredients prop is an array and I want to map it too. How can I do it?
Any help would be great, thanks!
Just map it the same way you do with recipes
<div>
<h2>Recipes</h2>
<ul>
{this.state.recipes.map(recipe =>
<li key={recipe._id}>
{recipe.title}<br/>
{recipe.category}<br/>
{recipe.ingredients.map(ingredient => (
<span>{ingredient}</span>
))}
<br/>{recipe.preparation}
</li>
)}
</ul>
</div>
just map it again
<div>
<h2>Recipes</h2>
<ul>
{this.state.recipes.map(recipe =>
<li key={recipe._id}>
{recipe.title}<br/>
{recipe.category}<br/>
{recipe.ingredients.map(k =>(
<span key={k._id}>{k}<span>
)<br/>
{recipe.preparation}
</li>
)}
</ul>
</div>
<div>
<h2>Recipes</h2>
<ul>
{this.state.recipes.map(recipe =>
<li key={recipe._id}>
{recipe.title}<br/>{recipe.category}<br/>
{ recipe.ingredients.map(ingredient =>
<span>{ingredient}</span>
)}
<br/>{recipe.preparation}
</li>
)}
</ul>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You can also use .map() there by calling recipe.ingredients.map() as the following:
{this.state.recipes.map(recipe =>
<li key={recipe._id}>
{recipe.title}<br/>{recipe.category}<br/>
{
recipe.ingredients.map((elem, index) =>
<div key={index}>
{elem /* or maybe elem['your-property-name'] if it's not a string */}
</div>
)
}<br/>{recipe.preparation}
</li>
)}
Also don't forget to add key={index} there to avoid the warning based on Lists and Keys.

if statement doing exact opposite of what I am expecting

I am sure this is something really easy but for some reason I can't figure out why I am getting the opposite result. If I use the code as below it returns the second if result. If I change it to != null it returns the first if result.
import React from "react";
export default class ListButtons extends React.Component{
render(){
console.log(this.props.before); // null
if(this.props.before == null){
return (
<div>
<ul className="pagination">
<li>
Next
</li>
</ul>
</div>
)
}else{
return (
<div>
<ul className="pagination">
<li>
Previous
</li>
<li>
Next
</li>
</ul>
</div>
)
}
}
}
Try this:
import React from "react";
export default class ListButtons extends React.Component{
render(){
const content = !!this.props.before ? (
<li>
Previous
</li>
<li>
Next
</li>
) : (
<li>
Next
</li>
)
return (
<div>
<ul className="pagination">
{content}
</ul>
</div>
)
}
}
If that doesn't work, it may be an error with where/how this.props.before is set.

Categories

Resources