Conditional link in JSX - javascript

I'm trying to write a way to render links conditionally. I have the following function:
const renderLinkIf = (content, condition, href) => {
if (condition) {
return (<Link to={href}>{content}</Link>);
}
return (content);
};
With very simple tasks it works:
{ renderLinkIf('test', true, '/dashboard') }
However, I can't figure out how to render more complex contents:
{renderLinkIf(
<span className={sectionCompleted(30) ? 'completed' : null}>
{sectionCompleted(30) ? <CheckIcon /> : <HeaderPersonalInfo />}
</span> Personal Info,
true,
'/dashboard',
)}
I just get Syntax Errors.
How can I pass more complex JSX through renderLinkIf to be rendered?

I believe you'd have to wrap the <span> and the text Personal Info in a single element for react. Other than that, I don't see any obvious errors:
{renderLinkIf(
<span><span className={sectionCompleted(30) ? 'completed' : null}>
{sectionCompleted(30) ? <CheckIcon /> : <HeaderPersonalInfo />}
</span> Personal Info</span>,
true,
'/dashboard',
)}

Related

Render react icon based on comparison with array

I currently have the following array with data:
const colors = {
Meeting: ["#D09FE8", GrGroup],
"1on1": ["#86DB43", MdLocalLibrary],
Review: ["#B22F5E", MdRateReview],
"Team Collaboration": ["#B22F5E", RiStackshareFill],
"Feature Review": ["#B22F5E", MdFeaturedPlayList],
};
My react component is receiving a prop which contains a string of the meeting type, for example "Meeting". Based on that prop, i want to render a react icon.
Changing the background color is fine, I just did it as follows:
const style = {
background: colors[`${eventData.meetingType}`][0],
}
But when trying to render the react icon, how can i do that here?
<div className="text-sm">{RENDER THE REACT ICON HERE}</div>
You can conditionally render components using conditional rendering:
<div className="text-sm">
{/* Think of this as an if statement. */}
{ (meetingType === something) && (
<img src={iconASrc} />
)}
{/* Conversely, this is an if-else statement. */}
{ (meetingType === something) ? (
<img src={iconASrc} />
) : (
<img src={iconBSrc} />
)}
{/* Or, even better */}
<img src={(meetingType === something) ? iconASrc : iconBSrc} />
</div>
See ternary operator for the latter two options.

"react" Each child in a list should have a unique "key" prop

An error seems to occur because the key value is not entered in the map function, but I do not know how to modify the code.
The array is structured like this:
const tabContArr=[
{
tabTitle:(
<span className={activeIndex===0 ? "is-active" : ""} onClick={()=>tabClickHandler(0)}>0</span>
),
},
{
tabTitle:(
<span className={activeIndex===1 ? "is-active" : ""} onClick={()=>tabClickHandler(1)}>1</span>
),
},
{
tabTitle:(
<span className={activeIndex===2 ? "is-active" : ""} onClick={()=>tabClickHandler(2)}>2</span>
),
},
{
tabTitle:(
<span className={activeIndex===3 ? "is-active" : ""} onClick={()=>tabClickHandler(3)}>3</span>
),
}
];
An error occurs in the map function part.
{tabContArr.map((section)=>{
return section.tabTitle
})}
Try with React Fragments with a key attribute as mentioned in React docs
{tabContArr.map((section, index)=>{
return <React.Fragment key={`section-tab-${index}`}>{section.tabTitle}</React.Fragment>
})}
What you have done is not the right way.
If you have data, instead of passing the ReactElement into the array you should pass it into the map function like this:
{tabContArr.map((tab, index)=>{
return <span
className={activeIndex === index ? "is-active" : ""}
onClick={()=>tabClickHandler(index)}
key={`tab-${index}`}>index</span>
})}

Mapping a different icon depending on property value in React

https://codesandbox.io/s/r546o8v0kq
My above sandbox shows basic mapping of an array of items. This forms a list of notes, dates and an icon depending on what type of item it is.
I am working some logic that maps each item to find out what value it is, based on that I assign the value a string to complete the type of font awesome logo.
const noteType = _.uniq(notes.map(value => value.intelType));
const noteIcon = [
`${noteType}`.toUpperCase() == "EDUCATION"
? "paper-plane"
: `${noteType}`.toUpperCase() == "ELIGIBILITY"
? "heart"
: `${noteType}`.toUpperCase() == "GENERAL"
? "twitter"
: null
];
If "intelType" has a value of "education" it would return the "paper-plane" string to complete the icon. e.g fa fa-${noteIcon}
<List>
{notes.map((note, index) =>
note !== "" ? (
<React.Fragment>
<ListItem className="pl-0">
<i class={`fa fa-${noteIcon}`} aria-hidden="true" />
<ListItemText
secondary={moment(note.createdAt).format("DD-MMM-YYYY")}
/>
</ListItem>
<p>{note.note}</p>
<Divider />
</React.Fragment>
) : null
)}
</List>
Its not getting mapped and returning all three values, which does not meet any criteria therefore returns null as requested. I'm a bit stuck as what to do next here.
You can define an object that maps intel type to icon names:
const noteIconMap =
{ "EDUCATION": "paper-plane",
"ELIGIBILITY": "heart",
"GENERAL": "twitter",
};
And look it up this way inside the render:
<i class={`fa fa-${noteIconMap[note.intelType.toUpperCase()]}`} aria-hidden="true" />
Although, beware, if there is a case where note can have intelType undefined, toUpperCase call will throw an error.
Here's a link to working modified sandbox:
https://codesandbox.io/s/ojz2lzz03z
I'm not sure what's going on with this bit of code:
const noteIcon = [
`${noteType}`.toUpperCase() == "EDUCATION"
? "paper-plane"
: `${noteType}`.toUpperCase() == "ELIGIBILITY"
? "heart"
: `${noteType}`.toUpperCase() == "GENERAL"
? "twitter"
: null
]
But it makes more sense to me to store that information as an object and then access the icon type that way.
const icons = {
EDUCATION: 'paper-plane',
ELIGIBILITY: 'heart',
GENERAL: 'twitter'
}
And then going:
icons[noteType]
You need to get an icon in the map for each note.intelType. Since you're passing an array of ids, none of the icons is matched, and the result is always null.
A simple solution is to create a Map of types to icons (iconsMap), and get the icon from the Map using note.intelType.
btw - currently note.intelType us always uppercase, so you don't need to transform it.
sandbox
const iconsMap = new Map([
['EDUCATION', 'paper-plane'],
['ELIGIBILITY', 'heart'],
['GENERAL', 'twitter'],
]);
class Notes extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const { notes, classes } = this.props;
return (
<React.Fragment>
<List>
{notes.map((note, index) =>
note !== "" ? (
<React.Fragment>
<ListItem className="pl-0">
<i class={`fa fa-${iconsMap.get(note.intelType)}`} aria-hidden="true" />
<ListItemText
secondary={moment(note.createdAt).format("DD-MMM-YYYY")}
/>
</ListItem>
<p>{note.note}</p>
<Divider />
</React.Fragment>
) : null
)}
</List>
</React.Fragment>
);
}
}

es6 literal string concatenation as prop in react

Obviously below code doesn't work, I don't know how to do it to be honest.
<div>
<Dropdown labelName="Settings" listItems={[
{name:'View Listing', handlerName:'view_listing'},
{name:'Edit Listing', handlerName:'edit_listing'},
{name:`${this.state.spaceStatus} === 'active' ? 'Deactivate Listing': 'Activate Listing'`, handlerName:'deactivate_listing'},
]}/>
</div>
I can do my logic out side but was trying to do it inline, not sure this is workable or not.
You've just ended the template literal token too soon. Your whole conditional operator expression should be in the ${...}, just move the } to after the end of 'Activate Listing':
<div>
<Dropdown labelName="Settings" listItems={[
{name:'View Listing', handlerName:'view_listing'},
{name:'Edit Listing', handlerName:'edit_listing'},
{name:`${this.state.spaceStatus === 'active' ? 'Deactivate Listing': 'Activate Listing'}`, handlerName:'deactivate_listing'},
// >>>---------------------------------------------------------------------------------^
]}/>
</div>

How to do a nested if else statement in ReactJS JSX?

I wanted to know if its possible to do nested if else if in ReactJS JSX?
I have tried various different ways and I am unable to get it to work.
I am looking for
if (x) {
loading screen
} else {
if (y) {
possible title if we need it
}
main
}
I have tried this but I can not get it to render. I have tried various ways. It always breaks once I add the nested if.
{
this.state.loadingPage ? (
<div>loading page</div>
) : (
<div>
this.otherCondition && <div>title</div>
<div>body</div>
</div>
);
}
Update
I ended up choosing the solution to move this to renderContent and call the function. Both of the answers did work though. I think I may use the inline solution if it is for a simple render and renderContent for more complicated cases.
Thank you
You need to wrap your title and body in a container. That could be a div. If you use a fragment instead, you'll have one less element in the dom.
{ this.state.loadingPage
? <span className="sr-only">Loading... Registered Devices</span>
: <>
{this.state.someBoolean
? <div>some title</div>
: null
}
<div>body</div>
</>
}
I would advise against nesting ternary statements because it's hard to read. Sometimes it's more elegant to "return early" than to use a ternary. Also, you can use isBool && component if you only want the true part of the ternary.
renderContent() {
if (this.state.loadingPage) {
return <span className="sr-only">Loading... Registered Devices</span>;
}
return (
<>
{this.state.someBoolean && <div>some title</div>}
<div>body</div>
</>
);
}
render() {
return <div className="outer-wrapper">{ this.renderContent() }</div>;
}
Caveat to the syntax someBoolean && "stuff": if by mistake, someBoolean is set to 0 or NaN, that Number will be rendered to the DOM. So if the "boolean" might be a falsy Number, it's safer to use (someBoolean ? "stuff" : null).
Instead of nesting ternary operators as it is often suggested or creating a separate function that will not be reused anywhere else, you can simply call an inline expression:
<div className="some-container">
{
(() => {
if (conditionOne)
return <span>One</span>
if (conditionTwo)
return <span>Two</span>
else (conditionOne)
return <span>Three</span>
})()
}
</div>
You can check multiple conditions to render components accordingly like below:
this.state.route === 'projects'
?
<div> <Navigation onRouteChange={this.onRouteChange}/> Projects</div>
:
this.state.route === 'about'
?
<div> <Navigation onRouteChange={this.onRouteChange}/> About</div>
:
this.state.route === 'contact'
?
<div> <Navigation onRouteChange={this.onRouteChange}/> Contact</div>
:
<p> default </p>
You can nest as many statements as possible. please follow this code assuming that this.state.firstTestValue, this.state.someTestValue and this.state.thirdValueTest are your test values from the state.
{this.state.firstTestValue
? <div >First Title</div>
: [
this.state.someTestValue
? <div>Second Title</div>
: [
this.state.thirdValueTest
? <div>Some Third Title</div>
: <div>Last Title</div>
]
]
}
Your code in the alternative is not valid JavaScript/JSX expression:
(
this.state.someBoolean ?
(<div>some title</div>):(<div>some other title</div>)
<div>body</div>
)
Lets simplify this to
(
true ? 42 : 21
3
)
This throws the error
Uncaught SyntaxError: Unexpected number(…)
You cannot just have two expression next to each other.
Instead you have to return a single value from the false branch of the conditional operator. You can do this by simply putting it in another JSX element:
(
<div>
{this.state.someBoolean ? (<div>some title</div>) : (<div>some other title</div>)}
<div>body</div>
</div>
)
If you want to only show "body" when "some other title" is shown, you need to move <div>body</div> into the false branch of the conditional operator:
(
this.state.someBoolean ?
(<div>some title</div>) :
(<div>
<div>some other title</div>
<div>body</div>
</div>)
)
Or maybe you want
(
<div>
{this.state.someBoolean ? (<div>some title</div>) : null}
<div>body</body>
</div>
)
As the other answer suggest, there are various ways to do a nested if else in react. Which one to use depends on situation and preferences.
In my opinion, for best code readability, on this occassion it'd be best to move the content of else to separate function:
renderContent() {
return (
<div>
{ this.otherCondition && <div>title</div> }
<div>body</div>
</div>
);
}
render() {
return (
<div>
{ ... }
{
this.state.loadingPage ?
<div>loading page</div>
:
this.renderContent()
}
{ ... }
</div>
)
}
Or if it's simple enough (in case no other elements are rendered), I'd go with:
render() {
if (this.state.loadingPage) {
return (
<div>loading page</div>
)
}
return (
<div>
{ this.otherCondition && <div>title</div> }
<div>body</div>
</div>
);
}
Here's an article on conditional rendering in React, so please check it out if you're interested in more details on this topic.

Categories

Resources