Check if react element is empty - javascript

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.

Related

React component. Render specific element

Currently I've a component User, which renders 2 element -> username and avatar.
I'm getting the username and avatar perfectly, but I want to view only the username only
Is there any way to fetch only the username element ? Not with a profile picture.
//User component
const User = ({ username, profilePic }) => {
return (
<React.Fragment>
<Avatar name='user' src={profilePic.profile.image} alt="user_image" ml="5"/>
<Heading size={'sm'} ml="5">{username.username}</Heading>
</React.Fragment>
);
};
// Content Page
{group.members.map(member => {
return <React.Fragment key={member.id}>
<User username={member.user} profilePic={member.user}/>
</React.Fragment>
})}
You could add an extra prop renderAvatar and only display the avatar if the boolean is true with conditional rendering.
const User = ({ username, profilePic, renderAvatar }) => {
return (
<React.Fragment>
{renderAvatar && <Avatar name='user' src={profilePic.profile.image} alt="user_image" ml="5"/>}
<Heading size={'sm'} ml="5">{username.username}</Heading>
</React.Fragment>
);
};
You could use it like this.
<User username={member.user} profilePic={member.user} renderAvatar={false} />
<User username={member.user} profilePic={member.user} renderAvatar={true} />
Or just create a component that only renders the Heading.
const UserWithoutAvatar = ({ username }) => {
return <Heading size={'sm'} ml="5">{username.username}</Heading>
};
One option would be to conditionally render based on whether or not profilePic is provided at all. For example:
return (
<React.Fragment>
{ profilePic ?
<Avatar name='user' src={profilePic.profile.image} alt="user_image" ml="5"/>
: null
}
<Heading size={'sm'} ml="5">{username.username}</Heading>
</React.Fragment>
);
Then if you just don't provide profilePic it will be undefined:
<User username={member.user} />
As an aside, the code seems to be generating confusion around naming. For example:
{username.username}
A property called "username" implies that it is a string representing the user's name. But in this case username is an object containing a property called username? Does that property contain a string? Or another object?
Or here:
<User username={member.user} profilePic={member.user}/>
What is member.user? Is it a username? Is it a profile pic? Somehow it's both?
Clarity is important. If what you're actually passing to the component is a user object then call it that:
<User user={member.user} />
Alternatively, if the component is expecting a literal value for username and a literal value for profilePic then pass it those values:
<User username={member.user.username} profilePic={member.user.profile.image} />
Don't confuse your semantics. Confusion leads to bugs.

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

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

Component's prop value isn't showing in React.js

I'm trying to make a clone of Spotify where I'm using Spotify-web-API to access all the user data (id, name, playlist info, etc.) And to avoid prop drilling all these data are getting stored in Datalayer with the help of React-Context-api. I have a component Sidebar.js where all the playlist names are getting pulled from the API and will be shown in the sidebar. Inside this component, I also have another component SidebarOption.js to show names of playlists. Although I'm getting the playlist names from the API but when I passed those in the SidebarOption.js component as title prop it isn't showing there. I also console logged the names to be sure. The names are showing in my console but not through the mapping function in SidebarOption component. I've attached a screenshot of what I'm getting in this link Screenshot of console log.
You'll see that outside the mapping function any prop value passed in the SidebarOption component is showing but not the ones inside the mapping function. So any help would be appreciated.
Sidebar.js
function SideBar() {
const [{ playlists }, dispatch] = UseDataLayerValue();
console.log("user playlist from sidebar.js", playlists);
return (
<div className="sidebar">
<img
className="sidebar__logo"
src="https://getheavy.com/wp-content/uploads/2019/12/spotify2019-830x350.jpg"
alt="spotify-logo"
/>
<SidebarOption Icon={HomeIcon} title="Home" />
<SidebarOption Icon={SearchIcon} title="Search" />
<SidebarOption Icon={LibraryMusicIcon} title="Your Library" />
<br />
<strong className="sidebar__title">PLAYLISTS</strong>
<hr />
{playlists?.items?.map((playlist) => {
console.log(playlist.name);
<h2>hi</h2>;
<SidebarOption title={playlist.name} />;
})}
</div>
);
}
SidebarOption.js
function SidebarOption({ title, Icon }) {
return (
<div className="sidebarOption">
{Icon && <Icon className="sidebarOption__icon" />}
{Icon ? <h4>{title}</h4> : <p>{title}</p>}
</div>
);
}
The main problem is, you're using a map. But, you're not returning the value.
You have:
{playlists?.items?.map((playlist) => {
console.log(playlist.name);
<h2>hi</h2>;
<SidebarOption title={playlist.name} />;
})}
Should be:
{playlists?.items?.map((playlist) => {
console.log(playlist.name);
<h2>hi</h2>;
return <SidebarOption title={playlist.name} />;
})}
Here is the Codesandbox to notice:
https://codesandbox.io/s/wizardly-sun-iku99?file=/src/App.js
the problem here is that you are not returning the SidebarOption in the map function you instead can do it like that
{playlists?.items?.map((playlist) => {
console.log(playlist.name);
return (
<>
<h2>hi</h2>;
<SidebarOption title={playlist.name} />;
</>
);
})}
or
{playlists?.items?.map((playlist) => (
<>
<h2>hi</h2>;
<SidebarOption title={playlist.name} />;
</>
))}

Rendering a list into ListItems and Links

class Glass extends React.Component {
constructor(props) {
super(props);
this.state = {
names: [{n: "Gemma", num: "01"}, {n: "Katie", num: "02"}],
}
}
render() {
const { list } = this.state;
return (
<div>
<List component="hello">
{list.map(name => {
return (
<ListItem key={name.num}>
<Link to="/glassmates/" + name.num > //I keep getting error here saying unexpected token.
{name.n}
</Link>
</ListItem>
<Divider /> // also getting errror here saying adjacent jsx elements must be wrapped in an enclosing tag
)
})}
</List>
</div>
);
}
}
I listed some errors I'm getting in the code, but I'm confused in general whether this is how you render a list into Links inside a ListItem and a List component...
{list.map(name => {
return (
<div>
<ListItem key={name.num}>
<Link to="/glassmates/" + name.num > //Because your state not contains list==>names
{name.n}
</Link>
</ListItem>
<Divider /> // JSX should have on parent
</div>
)
})}
Concerning you errors: for the first error, you should be using string interpolation, i.e:
<Link to=`/glassmates/${name.num}` >
Secondly, as stated adjacent jsx elements must be wrapped in an enclosing tag.
Which means you could/should wrap your elements in a div:
<div> // parent
...
<Divider />
</div>

React passing argument by path with router and does not render properly

I have the problem in React, with passing id argument using path in router.
That is my code(index.js):
class App extends React.Component{
render() {
return (
<Router>
<div>
<Home>
<Switch>
<Route path="/mail/:id" component={Mail} />
<Route path="/mail" component={Mail} />
</Switch>
</Home>
</div>
</Router>
)
}}
Next, in Mail.jsx, I have the render function:
render() {
const { activeItem } = this.state;
return (
<Grid>
<Grid.Column width={4}>
<Menu fluid vertical tabular>
<Menu.Item name='mail1' active={activeItem === 'mail1'} onClick={this.handleItemClick}> {this.props.match.params.id}</Menu.Item>
</Menu>
</Grid.Column>
<Grid.Column stretched width={12}>
<div>
<Segment>
{this.state.mails.length > 0 && this.state.mails ? this.state.mails[0].name : null}
</Segment>
</div>
</Grid.Column>
</Grid>
)
}
The problem is, that when I write http://localhost:3000/mail I see the "this.state.mails[0].name" and no id ofcourse because I didnt pass one, but when I write http://localhost:3000/mail/5 I can see the number five on the page, but no "this.state.mails[0].name" anymore, why is that?
And getting data from json file:
componentDidMount(){
axios.get("db.json")
.then( (response) => {
console.log(response);
this.setState({
mails: response.data.mail,
total: response.data.mail.length
});
})
.catch(function (error) {
console.log('error');
console.log(error);
});
}
Edit, that is my render function in Home.jsx:
render() {
const { activeItem } = this.state;
return(
<div>
<div>
<MenuExampleSecondaryPointing/>
</div>
<div>
{this.props.children}
</div>
</div>
);
}
I found an answer from a friend, that is that simple as that:
" axios.get("db.json")" should be "axios.get("/db.json")"
because with "/" I search in all catalogs for that file

Categories