React conditional rendering of multiple child components - javascript

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

Related

React map function not assinging keys properly

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

Is there a React higher order component to replace someData.map(functionReturningComponent)?

I find code like this to be hard to read:
<Row>
<Column>
{someData.map((value, idx) => (
<Component key={idx}>
<ChildComponent value={value.foo}/>
<ChildComponentTwo value={value.bar}/>
</Component>
)}
</Column>
</Row>
Is there a HOC to replace Array.prototype.map? Say, List that takes a component and generates N components given an array of N items of data mappable to the props of the passed-in component?
declare const data: {value:string}[]
const MyComponentList = List(MyComponent)
<Row>
<Column>
<MyComponentList data={data}/>
</Column>
</Row>
and then Component = ({foo, bar}) => (<>
<ChildComponent value={foo}/>
<ChildComponentTwo value={bar}/>
</>)
Now I don't need map anywhere except I guess in the definition of List. The JSX is much easier to read. But when I search things like "HOC react replace array map JSX" I don't find anything. I suppose List should also inject key={idx} into the "child" components where idx comes from dataArray.map(dataToGoToChild, idx) => <ChildComponent key={idx} {...dataToGoToChild}/>

How to make React page render only change component instead of the whole page?

So I have a toggle looking like this (see below), but the page always re-render the whole thing on the first time I click on the toggle.
export default function Toggle({isChecked, label}: Props) {
return (
<Wrapper>
<Switch isChecked={isChecked}>
<span />
</Switch>
{label}
</Wrapper>
)
}
Then another component which is using this Toggle component
export default function ToggleBox({isChecked, label, children}: Props) {
return (
<Wrapper>
<Toggle isChecked={isChecked} label={label} />
<Content>{children}</Content>
</Wrapper>
)
}
There is a layout
export default function Layout({...someprops bla bla, children}: Props) {
<Wrapper>
<DesktopImg>
<ImageWrapper>
<Image src={image.url} alt={`${headline} image`} layout="fill" />
</ImageWrapper>
</DesktopImg>
<div>
<Content>
{back && backButton}
<MobileImg>
<Image
src={image.url}
alt={`${headline} image`}
width={image.width}
height={image.height}
/>
</MobileImg>
{headline}
<P gutterSize="medium">{description}</P>
</Content>
<ChildrenContainer>{children}</ChildrenContainer>
</div>
</Wrapper>
}
Then finally the page which use the ToggleBox.
export default function Index({isChecked, label, children}: Props) {
const [check, setCheck] = useState(false)
return (
<Layout>
<div onClick={() => setCheck(!check)}>
<ToggleBox label="some label..." isChecked={check}>
//sometext..
</ToggleBox>
</div>
<Button onClick={nextPage} disabled={!check}>
Next
</Button>
</Layout>
)
}
I kinda tried to use the React.memo method but it doesnt seem to work. Any suggestion to make the page not re-render the whole thing but just the toggle?
Move your state further down the tree, you want it to be as close to the component(s) it impacts as possible, again this will probably require breaking out into smaller components, for example, break out the following into a seperate component -
const NewToggleComponent = () => {
const [check, setCheck] = useState(false)
return (
<div onClick={() => setCheck(!check)}>
<ToggleBox label="some label..." isChecked={check}>
//sometext..
</ToggleBox>
</div>
<Button onClick={nextPage} disabled={!check}>
Next
</Button>
)
}
remove state from the top level component, and use this component in your top level component -
...
<NewToggleComponent />
...

Render multiple components with a single ternary operator

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 />
)}

Best way to render multiple child components

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>

Categories