If currentProfiles.length > 0, I'd like to map over an array named profiles and render a profile component for each profile, and render a pagination component below the profiles. I tried this with a single ternary operator, but this results in only the pagination component being rendered.
{currentProfiles.length > 0 ? (
(currentProfiles.map(profile => (
<ProfileItem key={profile._id} profile={profile} />
)),
(
<Pagination
profilesPerPage={profilesPerPage}
totalProfiles={profiles.length}
/>
))
) : (
<Spinner />
)}
If I use two separate ternary operators, I get the list of profiles and pagination as expected, but can I do both things with a single conditional operator?
Your code just needs some restructuring. If you wrap the mapped profiles and pagination components in a parent fragment or other element, it's easy. Note, too, that the first example below still retains the ternary, as requested.
return (
<div className="App">
{currentProfiles.length ? (
<>
{currentProfiles.map(p => (
<Profile {...p} />
))}
<Pagination profilesPerPage={2} totalProfiles={totalProfiles} />
</>
) : (
<p>Loading...</p>
)}
</div>
);
However, you have a few options aside from wrapping them in a non-rendered Fragment or its shorthand derivative. You could also use an actual element, such as a div. Or even omit the parent entirely and place your logic within an array, as in:
<div className="App">
{currentProfiles.length ? [
currentProfiles.map(p => (
<Profile {...p} />
)),
<Pagination profilesPerPage={2} totalProfiles={totalProfiles} />
] : <p>Loading...</p>}
</div>
Always remember that, unless you utilize the second approach, you'll need to ensure siblings share a common parent.
Working example.
You can use an array or a fragment https://reactjs.org/docs/fragments.html
{currentProfiles.length > 0 ? (
<>
currentProfiles.map(profile => (
<ProfileItem key={profile._id} profile={profile} />
)
<Pagination
profilesPerPage={profilesPerPage}
totalProfiles={profiles.length}
/>
</>
) : (
<Spinner />
)}
Related
I am trying to render different components via an if-statement, that depends on the path name. See return statement below.
return (
<>
<Navbar style={{position:"sticky"}} ammount={safedIds.length} setShowCalendar={setShowCalendar} />
<DateBar date={currentDate? currentDate:"no date available"} />
{eventsPrep.length > 0 ?
window.location.pathname === "/calendar"?
<Calendar />
:
<Home events={eventsPrep} addSafedId={addSafedId} />
: <></>}
</>
);
I know that the path updates but the components do not.
How do I fix this?
for some reason, I get the "each child in a list should have unique key" error when returning the following code. I do not understand why this happens, as I specifically assign the key during mapping:
return (
<>
{sortfeedCards(feedCards)}
{loggedIn === true ? (feedCardsMod.map((card, index) => (
<>
<p>{index}</p>
<FeedCard key={index} cardData={card} loggedIn={loggedIn} />
</>
))) : ('')}
</>
)
And here's what the render looks like.. it seems to me that the index-variable does work:
Many Thanks in advance!
The key needs to be on the outermost element, so on the Fragment, not the FeedCard:
feedCardsMod.map((card, index) => (
<React.Fragment key={index}>
<p>{index}</p>
<FeedCard cardData={card} loggedIn={loggedIn} />
<React.Fragment/>
))
(The shorthand syntax <></> for fragments doesn't allow keys, so i switched to using React.Fragment explicitly)
Try this:
return (
<>
{sortfeedCards(feedCards)}
{loggedIn === true ? (feedCardsMod.map((card, index) => (
<div key={index}>
<p>{index}</p>
<FeedCard cardData={card} loggedIn={loggedIn} />
</div>
))) : ('')}
</>
)
Keys must be unique amongst the enclosing tags:
https://reactjs.org/docs/lists-and-keys.html
cannot seem to find a fix to trying to have a JSX element within a JSX element that are both being mapped. I am getting return error 'Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>?'
I know that the return function needs to return one element as a whole, which I believe I am doing with top most div. The outer mapping is mapping an array of strings and the inner mapping is mapping a web pack that has been imported. I am pulling the outer mapping of strings from my redux state and I have verified that it works and the web pack is also mapping correctly, I just cannot get them to map together. In my head I just see this as basically an outerloop and then an inner loop. I will take any advice.
I am guessing there is a simple answer to this question, just cannot figure it out.
Here is my functional component:
function UserLesson() {
const classes = useStyles();
const selectLessons = useSelector(state => state.redu.userLessons)
return (
<div className={classes.root}>
{selectLessons.map((title) => (
<h2>{title}</h2>
<GridList className={classes.gridList} cols={2.5}>
{searchResources.result.map((resource) => (
<GridListTile key={resource.media}>
<img src={resource.media} alt={resource.title} />
<GridListTileBar
title={resource.title}
classes={{
root: classes.titleBar,
title: classes.title,
}}
actionIcon={
<IconButton aria-label={`star ${resource.title}`}>
<StarBorderIcon className={classes.title} />
</IconButton>
}
/>
</GridListTile>
))}
</GridList>
))}
</div>
);
}
To return multiple elements, you need to wrap them in a parent element. Below are few ways to do that:
By Adding an extra parent element:
return (
<div>
<Child/>
<Child/>
</div>
)
Without adding any extra parent element:
return (
<React.Fragment>
<Child/>
<Child/>
</React.Fragment>
)
Or using shorthand of Fragment:
return (
<>
<Child/>
<Child/>
</>
)
Hence, you can do this to fix your problem:
{selectLessons.map((title) => (
<React.Fragment key={some key here}>
<h2>{title}</h2>
<GridList className={clas....
....
</React.Fragment>
...
I just started learning ReactJS and am curious to know the best practice to rendering multiple different child components. What is the right way and most efficient way to do so? This is my current render function;
render() {
return (
<div>
<Display
image={this.state.professional.image}
username={this.state.professional.username}
/>
<div className="container">
<Navbar
brand_name={!!this.state.professional.brand_name}
username={!!this.state.professional.username}
description={!!this.state.professional.description}
menu={!!this.state.professional.menu}
reviews={!!this.state.reviews}
photos={!!this.state.professional.photos}
email={!!this.state.professional.email}
address={!!this.state.professional.service_addresses}
/>
<section className="artist-page">
<div className="container">
<About
description={this.state.professional.description}
accolades={this.state.professional.accolades}
cancellation={this.state.professional.lead_time_cancellation}
rules={this.state.professional.service_rules}
faqs={this.state.professional.faqs}
/>
{(this.state.professional.menu || this.state.professional.offers)
&&
<Services
services={this.state.professional.menu}
offers={this.state.offers}
/>}
{!!this.state.photos && <Portfolio photos={this.state.photos} />}
{!!this.state.reviews && <Reviews reviews={this.state.reviews} score={this.state.professional.rating_overall_stats} count={this.state.professional.review_count} />}
{(!!this.state.professional.email || !!this.state.professional.address) && <Contact
address={this.state.professional.service_addresses[0]}
name={!!this.state.professional.brand_name ? this.state.professional.brand_name : this.state.professional.username}
/>}
</div>
</section>
</div>
</div>
);
}
Think about what needs to be a state and what should be props.
It seems like this parent component that you pasted here is storing information of all the children as state, before passing down to its children as props.
Are you sure this parent needs to know anything about username, description, menu, etc of <Navbar>? If it doesn't, then save them as Navbar's own states.
You can pass down entire this.state.professional object as props.
<About professional = {this.state.professional}/>
Then in your About component, you can take apart the object and use it accordingly:
<p>{ this.props.professional.description }</p>
You can consider destructuring for readability
const {description, rules} = this.state.professional
return (
<About description={description} rules={rules}/>
)
Fix your <Navbar>
Personally I think that it looks weird and unreadable when you pass down boolean like that. You can consider this:
<Navbar reviews={this.state.reviews} {...this.state.professional} />
Then do your boolean evaluation inside Navbar component itself, here's a few example:
const {username, description, reviews} = this.props
<p>{ reviews || 'No reviews' }</p>
<p>{ username ? formatName(username) : "" }</p>
<p>{ description && 'Has description!' }</p>
I'm trying to render multiple child components depending on state however I'm only able to return one child component (SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag)
Each child component passes the same props, how could this code be kept DRY?
Works
export default ({changeState, myState, handleClick}) => (
<Navigation>
<span>Navigation</span>
<button onClick={() => changeState()}>Navigation</button>
{ myState ?
<NavigationItem handleClick={handleClick} title={'#Link-1'} />
: null
}
</Navigation>
)
Don't
export default ({changeState, myState, handleClick}) => (
<Navigation>
<h1>Navigation</h1>
<button onClick={() => changeState()}>Navigation</button>
{ myState ?
<NavigationItem handleClick={handleClick} title={'#Link-1'} />
<NavigationItem handleClick={handleClick} title={'#Link-2'} />
<NavigationItem handleClick={handleClick} title={'#Link-3'} />
: null
}
</Navigation>
)
Directly we can't return more than one elements.
Possible Solutions:
1- Either you need to wrap all the elements in a div or any other wrapper element.
2- We can return an array of multiple elements also, So put all the items in an array, and return the array.
Like this:
{myState ?
[
<NavigationItem handleClick={handleClick} title={'#Link-1'} />,
<NavigationItem handleClick={handleClick} title={'#Link-2'} />,
<NavigationItem handleClick={handleClick} title={'#Link-3'} />
]
: null
}
Check this example:
let b = true ? [1,2,3,4]: null;
console.log('b = ', b);
This will throw error:
let b = true? 1 2 3 4: null;
console.log('b = ', b);
You can also use <Fragment> from ReactJS: https://reactjs.org/docs/fragments.html
The problem about wrapping all the elements with a <div>, is that you are adding more elements to the DOM, and sometimes it's impossible (for example, when you are rendering a <td> or <tr> inside a <table>. So, here is where <Fragment> comes to help us.
Just wrap all those elements in a <Fragment> and it'll be enough.
Meaning:
{ myState &&
<Fragment>
<NavigationItem handleClick={handleClick} title={'#Link-1'} />
<NavigationItem handleClick={handleClick} title={'#Link-2'} />
<NavigationItem handleClick={handleClick} title={'#Link-3'} />
</Fragment>
}
Anyways, this another "Conditional Rendering" approach is better in "code readability" sense: https://medium.com/#BrodaNoel/conditional-rendering-in-react-and-jsx-the-solution-7c80beba1e36
It basically proposes the use of a <Conditional> element, like:
<Conditional if={myState}>
<NavigationItem handleClick={handleClick} title={'#Link-1'} />,
<NavigationItem handleClick={handleClick} title={'#Link-2'} />,
<NavigationItem handleClick={handleClick} title={'#Link-3'} />
</Conditional>
^ This looks better for my eyes :D