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.
Related
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} />;
</>
))}
I am working on application where I have different multi role ( admin, user,manager ) I want to protect route of admin from manager and general user also render UI based on user role . I tried but I am failed could someone please help me how to achieve my goal .
Example will be appreciated
Thanks
Please beware that this is not an optimal solution and only meant to give you some ideas.
Create a file RoleBasedRouting.jsx
function RoleBasedRouting({
component: Component, roles, ...rest
}) {
return (
<>
{ grantPermission(roles) && (
<Route
{...rest}
render={(props) => (
<>
<Component {...props} />
</>
)}
/>
)}
{
!grantPermission(roles) && (
<Route
render={() => (
<>
<Unauthorized /> // Unauthorized Page View (skippable)
</>
)}
/>
)
}
</>
);
}
Use it in your Router like this -
<Switch>
<RoleBasedRouting exact path="/admin" component={AdminPage} roles={['ROLE_ADMIN']} />
<RoleBasedRouting exact path="/user" component={UserPage} roles={['ROLE_USER']} />
<RoleBasedRouting exact path="/manager" component={ManagerPage} roles={['ROLE_Manager']} />
...............
</Switch>
In grantPermission function, check if the logged in user has the required roles. Sample -
export const grantPermission = (requestedRoles) => {
const permittedRoles = JSON.parse(localStorage.getItem('userRoles'));
// in case of multiple roles, if one of the permittedRoles is present in requestedRoles, return true;
return false;
};
To render UI conditionally, you can do basically the same thing. Write a component UnlockAccess -
const UnlockAccess = ({ children, request }) => {
const permission = grantPermission(request); // request = ['ROLE_ADMIN'] / ['ROLE_USER'] / ['ROLE_MANAGER']
return (
<>
{permission && children}
</>
);
};
Now, Use UnlockAccess component in the Dashboard page like this -
<Dashboard>
<UnlockAccess request={['ROLE_ADMIN']}>
<>
{/*Write code/components for Admin Dashboard*/}
</>
</UnlockAccess>
<UnlockAccess request={['ROLE_USER']}>
<>
{/*Write code/components for User Dashboard*/}
</>
</UnlockAccess>
<UnlockAccess request={['ROLE_MANAGER']}>
<>
{/*Write code/components for Manager Dashboard*/}
</>
</UnlockAccess>
</Dashboard>
You should create different route components specifically for all roles, for example, AdminRoute, UserRoute, etc and in these components, you can check the weather login person is admin or a normal user.
or create a general route component and pass role and path there as props
You can use a HOC to check if the user accessing the route is allowed to access, i.e. if user is admin who is trying to access the admin dashboard is admin. If not then can redirect him to wherever you like.
export default function withAuth(WrappedComponent) {
const auth = (props) => {
return (
localStorage.getItem("userRole") === "admin" ?
<WrappedComponent {...props} /> :
<Redirect to = {{
pathname: "/protected/login"
}} />
)
}
return auth;
}
Or you can maintain a features array in localStorage with features you wanna give access to your user.
I created a basic component such as:
export default (props) => (
<TouchableOpacity {...props} style={styles.button}>
{props.title && <Text style={styles.text}>{props.title}</Text>}
{props.icon && <Icon name={props.icon} />}
</TouchableOpacity>
);
I can then call it with <Component title="Home" icon="home" /> for instance.
The problem is that passing {...props} to TouchableOpacity generate errors because it does not recognize title nor icon properly.
For instance:
JSON value 'Home' of type NSString cannot be converted to...
Is there a way to filter props so that I only pass valid ones for TouchableOpacity?
Transferring Props
Sometimes it's fragile and tedious to pass every property along. In that case you can use destructuring assignment with rest properties to extract a set of unknown properties.
List out all the properties that you would like to consume, followed by ...other.
var { checked, ...other } = props;
This ensures that you pass down all the props EXCEPT the ones you're
consuming yourself.
function FancyCheckbox(props) {
var { checked, ...other } = props;
var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
// `other` contains { onClick: console.log } but not the checked property
return (
<div {...other} className={fancyClass} />
);
}
ReactDOM.render(
<FancyCheckbox checked={true} onClick={console.log.bind(console)}>
Hello world!
</FancyCheckbox>,
document.getElementById('example')
);
Like Paul Mcloughlin, I would recommend using object destructuring along with a rest parameter. You can destructure your props object directly in your function parameters like so:
({title, icon, ...remainingProps}) => (...)
This extracts the title and icon props from your props object and passes the rest as remainingProps.
Your complete component would be:
export default ({title, icon, ...remainingProps}) => (
<TouchableOpacity {...remainingProps} style={styles.button}>
{title && <Text style={styles.text}>{title}</Text>}
{icon && <Icon name={icon} />}
</TouchableOpacity>
);
I have the following set up, with all the methods with .bind(this) in the constructor. So the user enters in username and password in the inputs and get updated in state, and the component gets re-rerendered and newly updated state this.props.username and this.props.password get passed down. I would then want to pass them into an action creator called this.props.registerUser() once Register button is clicked.
So I was wondering, what's the right practice to pass in the newly updated props to an action creator?
For example
Is it _handleRegister(this.props.username, this.props.password) then this.props.registerUser(this.props.username, this.props.password)? Or simply this.props.registerUser(this.props.username, this.props.password)? Or a combination of this._handleRegister(this.props.username, this.props.password) and former?
_handleRegister() {
this.props.registerUser()
}
render() {
return (
<View>
<TextInput
placeholder={'Username'}
onChangeText={...}
value={this.props.username}
/>
<TextInput
placeholder={'Password'}
onChangeText={...}
value={this.props.password}
/>
<TouchableHighlight onPress={this._handleRegister}>
<Text>Register</Text>
</TouchableHighlight>
</View>
)
}
}
Thank you
You don't need to pass prameters to _handleRegister
_handleRegister() {
const { username, password, registerUser } = this.props;
registerUser(username, password);
}
Additional tip: you can skip return keyword by doing:
render() (
<View>
...
</View>
)
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.