How to simply nested ternary logic in react while rendering different component - javascript

I have the following logic inside a react component where, I am rendering different component based on the boolean values. This peace of code is very difficult to understand. Are there anyways, I can simply that logic:
{isEnabled ? (
<>
{!loading ? (
<>
{items.length === 0 ? (
<>
<ComponentOne/>
<Container>
<img src={Image} alt="Image" />
</Container>
</>
) : (
<ComponentTwo/>
)}
</>
) : (
<div>
<LoadingComponent/>
</div>
)}
</>
) : (
<ComponentThree/>
)}

I'd probably split it up into seperate components and pass parameters down the component tree for example
{isEnabled ? <IsLoadingComponent loading={loading} items={items}> : <ComponentThree/>}

You might find it useful to split the component up into a "Loading" version and a "Loaded" version so you don't have to handle both states in the same component. Then the component basically just renders the "Loading" or "Loaded" version depending on the flag.
But even without that, you can at least make that easier to debug by using if/else if etc. and assigning to a temporary variable:
let comp;
if (isEnabled) {
if (loading) {
comp = <div>
<LoadingComponent/>
</div>;
} else if (items.length === 0) {
comp = <>
<ComponentOne/>
<Container>
<img src={Image} alt="Image" />
</Container>
</>;
} else {
comp = <ComponentTwo />;
}
} else {
comp = <ComponentThree />;
}
Then just
{comp}
where that nested conditional was.

I think you are making a simple thing very complicated. What we can do instead is that make use of "&&".
{ isEnabled && loading && <LoaderComponent /> }
{isEnabled && !items.length &&
<>
<ComponentOne/>
<Container>
<img src={Image} alt="Image" />
</Container>
</>
}
{isEnabled && items.length && <ComponentTwo/>}
{!isEnabled && <ComponentThree />}

Though I want to support the argument the others made (split into multiple components), you can already achieve a bit more readability by dropping unnecessary fragments (<></>) and/or parenthesis and by using "better"(opinion) indentation.
return (
isEnabled
? loading
? <div><LoadingComponent/></div>
: items.length === 0
? <> {/* this is the only place a fragment is actually needed */}
<ComponentOne/>
<Container>
<img src={Image} alt="Image"/>
</Container>
</>
: <ComponentTwo/>
: <ComponentThree/>
);
Alternatively, early returns do help a lot with readability. For example:
const SomeComponent = () => {
// ...snip...
if (!isEnabled) {
return <ComponentThree/>;
}
if (loading) {
return <div><LoadingComponent/></div>;
}
if (items.length > 0) {
return <ComponentThree/>;
}
return (
<>
<ComponentOne/>
<Container>
<img src={Image} alt="Image"/>
</Container>
</>
);
}

Related

Whole component re renders when modal opens in react

<ThemeProvider theme={theme}>
<GlobalStyle />
{componentName !== 'questionaire' &&
componentName !== 'activityResult' && <CardWrapper />}
<ErrorModal
errorModal={errorModal}
handleErrorModal={handleError}
errorMsg={error}
/>
{successModal && successMsg ? (
<SuccessModal successModal={successModal} successMsg={successMsg} />
) : (
<Grid
className="card-layout"
style={
componentName === 'questionaire'
? { margin: '20px', height: 'calc(100% - 40px)' }
: { margin: '30px 20px' }
}
>
{customTagProps.meterId && (
<CustomTag type={componentName} propData={customTagProps} />
)}
</Grid>
)}
</ThemeProvider>
I have a modal component, which on load re renders the entire component. I want to prevent the re render of the entire component.
lack of Information
may I only suggest you try using useRef instead of useState
you could use useMemo() to return the component

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

better jsx condition to order component

I have an icon position prop that decide whether it's placed on the left or on the right of the children. I have this working
<List>
{iconPosition === 'right' && (
<Text />
)}
{icon && (
<Icon />
)}
{iconPosition === 'left' && (
<Text />
)}
</List>
But I think it can be more simple, although in my opinion my above code is readable.
You can't do this any other way. You're using individual conditions with their own result.
Maybe you could place them on one line to make them more readable.
<List>
{iconPosition === 'right' && <Text />}
{icon && <Icon />}
{iconPosition === 'left' && <Text />}
</List>
or
render() {
const textLeft = (iconPosition === 'left');
const textRight = (iconPosition === 'right');
...
}
And use that. But that's mostly down to your preference / the coding style you and your colleagues are using..
One way to do it is to use css float (left/right) but it'll make the icon to be all the way to the right. Another way is to use flex box, take for example:
<div style="display: flex; flex-direction: row;">
<div style="flex: 0; order: 1;">(X)</div>
<div>content</div>
</div>
The (X) is the "icon". The order: 1; in the style moves it to the right, if you change it to 0 or ommit it, it'll be on the left.
so if you use this approach and put the text + icon in a flex row, all you need to do is:
<Icon style={{order: (iconPosition === 'right' ? 1 : 0)}} />
You could introduce a higher-order component to attempt to deal with it. It does add some complexity to the project though.
const WithConditionalRender = WrappedComponent => (
({ renderCondition, ...props }) => {
if (renderCondition) {
return <WrappedComponent { ...props } />;
}
return null;
}
);
const ConditionalText = WithConditionalRender(Text);
const ConditionalIcon = WithConditionalRender(Icon);
const Example = ({ icon, iconPosition }) => (
<List>
<ConditionalText renderCondition={ iconPosition === 'right' } />
<ConditionalIcon renderCondition={ icon } />
<ConditionalText renderCondition={ iconPosition === 'left' } />
</List>
);
This is a generic HOC and could be tweaked to be more specific.

Reactjs condition inside map function

I've looked at a bunch of questions here and read the docs over and over, however this just doesn't seem to want to work no matter what I do.
This is supposed to return one thing if X is true and return something else if it's not. It's inside a map function because I need this to be done for multiple things at once.
function ContentProcessing(props) {
return (
<div>
props.content.map(content => {
{content.type === "card" ? (
<Card title={content.title} />
) : (
<Content title={content.title} paragraph={content.guideline} />
)}
})
</div>
);
}
both <Card /> and <Content /> return one string
However I get the error
./src/App.js
Syntax error: /src/App.js: Unexpected token, expected , (79:13)
77 | <div>
78 | props.content.map(content => {
> 79 | {content.type === "card" ? (
| ^
80 | <Card title={content.title} />
81 | ) ? (
82 | <Content title={content.title} paragraph={content.guideline} />
I don't get why this isn't working.
Issues:
1- Use {} to put expressions inside jsx (to put map inside div).
2- you are using {} means block body of arrow function, so you need to use return inside the function body, otherwise by default map returns undefined.
3- You are using {} twice, so 2nd {} will be treated as object and content.type will be treated as key and that key is not valid, thats why you are getting error.
4- Forgot to define the key on elements.
Use this:
return (
<div>
{
props.content.map(content => content.type === "card" ? (
<Card title={content.title} />
) : (
<Content title={content.title} paragraph={content.guideline} />
)
)}
</div>
);
A couple of things are wrong I believe. You didn't add the curly braces in the first div. Inside the map you added two times the curly braces so you either remove one or add a return statement. You also added to "?" (the second one should be ":").
This should work:
function ContentProcessing(props) {
return (
<div>
{props.content.map(content =>
content.type === "card" ? <Card title={content.title} /> : <Content title={content.title} paragraph={content.guideline} />
)}
</div>
);
}
You can also add if else statements inside the map if you add braces:
function ContentProcessing(props) {
return (
<div>
{props.content.map((content) => {
if (content.type === "card") {
return (<Card title={content.title} />);
}
return (<Content title={content.title} paragraph={content.guideline} />);
})}
</div>
);
}
Your syntax for the ternary operator is wrong. You have condition ? a ? b. The correct syntax is condition ? a : b.
Try
function ContentProcessing(props) {
return (
<div>
{props.content.map(content =>
content.type === "card" ? (<Card title={content.title} />) :
(<Content title={content.title} paragraph={content.guideline} />)
)}
</div>
);
}
Multiple issues with the code.
return (
<div>
{props.content.map(content =>
content.type === "card" ? (
<Card title={content.title} />
) : (
<Content title={content.title} paragraph={content.guideline} />
)
)}
</div>
);
Extra brackets removed.
Conditional operator syntax was wrong.expression ? expression : expression

Check if react element is empty

I don't want to render the title when description is empty
var description = <MyElement />; // render will return nothing in render in some cases
if (!description) { // this will not work because its an object (reactelement)
return null;
}
<div>
{title}
{description}
</div>
Whats the correct way instead of !description to check if its empty?
var description, title;
if (this.props.description) {
description = <Description description={this.props.description} />;
if (this.props.title) {
title = <Title title={this.props.title} />;
}
}
<div>
{title}
{description}
</div>
Or:
render() {
const { description, title } = this.props;
return (
<div>
{title && description && <Title title={title} />}
{description && <Description description={description} />}
</div>
);
}
Imo it's better practice that if your description element isn't needed then it isn't rendered, rather than returning null in it's render. Since you would likely be sending the data through a prop. And likewise if you don't want to render this component at all, then that should happen in the parent.

Categories