best practise with optional parts on html rendering - reactjs

I was wondering of a good way to conditionnally render a list of items. Basically I want to render a warning message, if there's a warning, I want to render message to contain a list of all the problems, here is my current approach :
text = (
<div>Orange fields are not mandatory, are you sure you want to submit without :
<ul>
{(() => {
if (e.infos.error === "warning")
return <li>informations</li>
})()}
{(() => {
if (e.links.error === "warning")
return <li>links</li>
})()}
{(() => {
if (e.file.error === "warning")
return <li>file</li>
})()}
</ul>
</div>);
that's ugly, but I wanted to test something, so another approach I took was something like that :
function optionalWarning(props) {
if (props.error === "warning")
return <li>{props.message}</li>;
return null;
}
....
text = (
<div>Orange fields are not mandatory, are you sure you want to submit without :
<ul>
<optionalWarning error="e.infos.error" message="informations" />
<optionalWarning error="e.links.error" message="links" />
<optionalWarning error="e.file.error" message="file" />
</ul>
</div>);
This is prettier, but I don't like the fact that I have to make an external functions to do that, I suppose the best practise is the second one, but are there other ways to do that ?

Use logical operators - the right hand side of these statements will only be used if the left hand side is truthy.
Otherwise, if the left hand side is false, undefined or null, React won't render anything.
<div>Orange fields are not mandatory, are you sure you want to submit without :
<ul>
{e.infos.error === "warning" && <li>informations</li>}
{e.links.error === "warning" && <li>links</li>}
{e.file.error === "warning" && <li>file</li>}
</ul>
</div>
You have to be careful to always ensure a false, undefined or null result when your check fails - e.g. if you're checking the length of a list with {list.length && <Something/>}, when the list is empty this will evaluate to 0 and React will render it as text, whereas a check like {list.length > 0 && <Something/>} will work as you expect.

Use ternary operator for conditional rendering, it will be easy to write conditions inside JSX.
Like this:
<div>Orange fields are not mandatory, are you sure you want to submit without :
<ul>
{e.infos.error === "warning" ? <li>informations</li> : null }
{e.links.error === "warning" ? <li>links</li> : null}
{e.file.error === "warning" ? <li>file</li> : null}
</ul>
</div>

I would go for:
<ul>
{ e.infos.error === "warning" && <li>informations</li> }
{ e.links.error === "warning" && <li>links</li> }
{ e.file.error === "warning" && <li>file</li> }
</ul>

Related

How to render JSON attributes conditionally in React - Render if the attribute exists

I am reading a JSON file in react. In some records the attribute Attachments exists which is basically an array and in some records it doesn't. I am trying to apply an conditional render where it will check if the attribute exists in the record, then it will render it's elements otherwise it will skip the record. It is giving me an error of "Cannot read properties of undefined (reading 'map')". My code is below:
{data.map((record) => (
<tr>
<td>{record.UniqueName}</td>
<td>{record.StatusString}</td>
<React.Fragment>
if (!record.Attachments) {
<React.Fragment>
{record.Attachments.map((line, index) => (
<React.Fragment>
<td>{line.uniqueAttachmentId}</td>
<td>{line.Filename}</td>
<td>{line.id}</td>
</React.Fragment>
))}
</React.Fragment>
}
</React.Fragment>
</tr>
))}
And the JSON sample is here.
[
{
"StatusString": "Received",
"UniqueName": "PR95291",
"Attachments": [
{
"uniqueAttachmentId": "QUZLeUFPVkQzUEU0T1kyOmJuTXlNREl4THpBM0x6QTBMek13T1RNeE56UTJNUT09",
"Filename": "Q882106Q892EOa - IAG Bento Boxes - Perth.pdf",
"id": "bnMyMDIxLzA3LzA0LzMwOTMxNzQ2MQ=="
}
]
},
{
"CreateDate": "2021-07-05T05:45:34Z",
"UniqueName": "PR95291",
}
]
In the above JSON example, in the second record the "Attachments" attribute is missing.
Thanks in advance.
You need to correct your condition. I'm assuming your error is thrown at record.Attachments.map(e=>
if (record.Attachments) { // If it exists then run below code block
You are using ! means it will negate the condition value, means
you were checking for if record.attachment is not present then run the code.
SOLUTION
And you cannot use if condition within JSX you need to use ternary operator condition ? true : false or && binary add operator.
{
record.Attachments && record.Attachments.map(()=>{
// code
})
}
PS:
You can directly use React.Fragment as <> </>
{data.map((record) => (
<tr>
<td>{record.UniqueName}</td>
<td>{record.StatusString}</td>
<>
{record.Attachments &&
record.Attachments.map((line, index) => (
<>
<td>{line.uniqueAttachmentId}</td>
<td>{line.Filename}</td>
<td>{line.id}</td>
</>
))}
</>
</tr>
))}
for validation of array you can use { isArray(data) && data.map((record) => {
})
You can use ternary operator (?) for checking if key exists in your json or not
like record.UniqueName ? {record.UniqueName} : ""

react classname 3 conditional rendering hide with display none

I have 3 variables that I must evaluate to hide a div if a condition is met between these 3 variables, something like this:
appState.fullResults
appState.someResults
appState.otherResults
So, when appState.fullResults and appState.someResults come empty, but appState.otherResults has something, I need to hide a div, this code snippet is an example:
<div classname='container'>
<div classname='full-some'>
boxes
</div>
<divclassname='others'>
boxes
</div>
</div>
I try something like this, but dont work
className={`${
(!appState.someResults?.length > 0 && otherResults?.length > 0)
? 'container-boxcard'
: 'd-none'
}`}
I always get the same result: if othersResults brings something and someResults brings nothing, the "d-none" is not passed to hide it
https://imgur.com/bZBUGo9
Still evaluates false and applies the d-none class
Hope I have explained well, thank you for your help.
Try this :
const { fullResults, someResults, otherResults } = appState;
const className = !fullResults?.length && !someResults?.length
&& otherResults?.length ? 'd-none' : 'container-boxcard';
<YourComponent className={className} {...props} />
!appState.someResults?.length > 0 is hard to understand, I'd suggest you should change it to !appState.someResults?.length or appState.someResults?.length === 0 (you can choose one of them).
I haven't seen you declared otherResults anywhere else, so I'd assume that otherResults?.length > 0 should be appState.otherResults?.length (or appState.otherResults?.length > 0).
className={`${(!appState.someResults?.length && appState.otherResults?.length)
? 'd-none'
: 'container-boxcard'
}`}
when appState.fullResults and appState.someResults come empty, but appState.otherResults has something
According to your demonstration, you can follow this with 3 conditions
className={`${(!appState.someResults?.length && !appState.fullResults?.length && appState.otherResults?.length)
? 'd-none'
: 'container-boxcard'
}`}
finally it was a problem with the function that created the objects fullResults, someResults & otherResults, it was passing all the answer to the object, creating another object inside, that's why always the object even if it had no data inside, evaluated length greater than 0 because it had something inside,
setAppQuotationState({
loading: false,
fullResults: fullResults?.length > 0 ? [fullResults] : [],
someResults: someResults?.length > 0 ? [someResults] : [],
otherResults: otherResults?.length > 0 ? [otherResults] : [],
})
}
correcting that bug, the solutions that I indicated, worked well.

Can someone explain me the difference between both this if conditions in React?

state = {
count: 1,
};
render() {
let classes = "badge m-5 bg-";
let { count } = this.state;
count === 1 ? (classes += "success") : (classes += "warning");//1st Condition
classes+= (count===1)?"success" : "warning";//2nd Condition
return (
<div className="container">
<span style={this.styles} className={classes} id="bad">
Hello!
</span>
</div>
);
}
I understood how that if condition works(condition ? true: false), but in the 2nd condition how it is possibly working even after placing the classes+= even before mentioning the condition?
Let's break it down. Remember, our initial value of count is 1.
let classes = "badge m-5 bg-";
...
classes+= (count===1)?"success" : "warning";
Firstly what'll happen is that count===1 will be checked for strict equality (it takes precedence), which will yield true. Since our condition is true, and we're using ternary operator, the expression returns "success" string, i.e.,
this:
classes+= (count===1)?"success" : "warning";
becomes this:
classes+= "success";
"success" will be appended to the classes string, therefore classes will become badge m-5 bg-success
In 2nd Condition, tt related to javascript operator precedence. ?: before +=
In Javascript, ternary operator (?...:) takes precedence over assignment (+=), so the right hand of the += resolves first and append either "success" or "warning" to classes.
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table

program nav menu in react. How do I use logical statements to display html?

I'm using gatsby.js to make a site and using graphql to pull in data for a menu. The menu data is working fine however when I try to add a submemu with logical conditions it outputs the html instead of rendering it. So instead of showing the unordered submenu list, it outputs <ul> and </ul> on my site. (where the list items and main ul are working fine)
Here is my menu component code:
const WPMenu = () => (
<StaticQuery
query ={ graphql`
{
wordpressMenusMenusItems(name: {eq: "Main Menu"}) {
name
items {
slug
title
child_items {
slug
title
}
}
}
}`
}
render = { data => (
<nav role="navigation">
<ul>
{data.wordpressMenusMenusItems.items.map(item => (
<li key={item.title}>
{item.child_items ? <Link aria-haspopup="true" to={`/${item.slug}`}>{item.title}</Link> : <Link to={`/${item.slug}`}>{item.title}</Link>}
{item.child_items ? '<ul>' : "" } //this line is not working
{item.child_items && item.child_items.map ( child =>
<li key={child.title}><Link to={`/${child.slug}`} >{child.title}</Link></li>
)}
{item.child_items ? '</ul>' : "" } //this line is not working
</li>
))}
</ul>
</nav>
)}
/>
);
its the two commented lines {item.child_items ? '<ul>' : "" } //this line is not working
That are giving me problems.
I've tried with this: {item.child_items ? "<ul>" : "" }
and replacing quotes with code ticks as well:
{item.child_items ? `<ul>` : "" }
I'm new to js, so likely there's something I'm missing, but why is it interpreting it as code?
The problem is that is item.child_items is true if is an empty array [] since it is checking it reference exist or not;
you can use item.child_items.length > 0 as the condition or simply item.child_items.lengthsince 0 is equal to false in js 0 == false //true
{item.child_items.length && '<ul>'}
or
{item.child_items.length > 0 ? '<ul>' : ''}
check the snippet for more info:
const obj = { items : [] }
//even though it is empty it will give you true;
console.log( Boolean(obj.items) )
// not not something is boolean equivilant of that thing
console.log( !!obj.items )
//workaround? using length
//since 0 == false
console.log( "how about length?", Boolean(obj.items.length))
Caveat: if item.child_items doesn't have length property, that line would throw an exception (e.g. item.child_items being undefined or null)

React Carousel with Multiple Items

I'm having problems with my slider with ternary conditions, how may I solve it?
It considers that i'm closing the item before the last clause is done. This is a trying of build a multi-item carousel.
<Carousel className="col-md-7 col-11" indicators="true" controls="false">
{this.props.children.map((rooms,index) =>
(index === 0 || index % 3 === 0) ? <Carousel.Item><h1>First</h1> :
((index+1) % 3 === 0) ? <h1>Last</h1></Carousel.Item> : <h1>Middle</h1>
)
}
</Carousel>
The problem is that this isn't valid JSX.
You can't render an opening <Carousel.Item> tag without a closing tag as part of the same expression. It's clear what you're trying to do here but it can't work because the JSX compiler can't 'know' that the closing tag will ever be rendered, because that's dependant on children. You have to render opening and closing tags as part of the same expression in order for the JSX to compile.
Probably the cleanest thing is to do the grouping of the children in a separate function and then map the result, simply rendering each group inside <Carousel.Item></Carousel.Item>, like so:
function groupIntoThrees (children) {
const output = []
let currentGroup = []
children.forEach((child, index) => {
currentGroup.push(child)
if (index % 3 === 2) {
output.push(currentGroup)
currentGroup = []
}
})
return output
}
... later in render method ...
<Carousel className="col-md-7 col-11" indicators="true" controls="false">
{groupIntoThrees(this.props.children).map((group) => (
<Carousel.Item>
<h1>first: {group[0]}</h1>
<h1>middle: {group[1]}</h1>
<h1>last: {group[2]}</h1>
</Carousel.Item>
)}
</Carousel>

Resources