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} />;
</>
))}
Related
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.
I am working in an application where in a page it have a button. When I select the button it will go to another page.
I am using Push in UseHistory() to pass between pages. I know we can go to a specific div using anchor tag. But I don't want to use anchor tag. Can anyone help me how can I do that in react.
This is the button component I used to redirect to another page.
const { push } = useHistory();
return (
<>
{surveyDisableTime && <SurveyInfoBox />}
<EmptyList
image={<NoDataIcon />}
title={formatMessage({ id: 'event.survey_response.assign_survey.heading' })}
subtitle={formatMessage({ id: 'event.survey_response.assign_survey.subheading' })}
onButtonClick={() => push(AppRoute.editEvent.replace(':eventId', event.id), { from: pathname })}
buttonText={formatMessage({ id: 'event.survey_response.assign_survey.button_label' })}
disabled={surveyDisableTime}
/>
</>
);
This is the onclick function -> onButtonClick={() => push(AppRoute.editEvent.replace(':eventId', event.id), { from: pathname })}
In the editEvent I need to go to a specific component. How can I do that?
Lets say this is the component of the page the router will reload to,
return (
<div className={classes.container}>
<div className={classes.creatEventContainer}>
<div className={classes.formWrapper}>
<BasicInfoSection
institutionOptions={institutionOptions}
seasons={seasons || []}
isSeasonFieldDisabled={isSeasonFieldDisabled}
{...props}
/>
<EventLocation isVisible={!!isInPerson} errors={errors} register={register} {...props} />
<SurveySelection
isSurveyFieldDisabled={isSurveyFieldDisabled}
isVisible={!!isSurvey}
{...props}
/>
<DateAndTimeSection control={control} errors={errors} />
<NameRecordingsServicesSectionContainer
fields={fields}
remove={remove}
append={append}
languageVoices={languageVoices}
/>
</div>
</div>
</div>
)
I need to go to component.
Thankyou in advance.
try useNavigate() instead because the hook useHistory is not supported in react-router-dom v6 anymore!
if filledOutTheSurvey is false then return Survey component
but if the user fills out then the state of filledOutSurvey would be true and AnotherComponent would render
...
const [filledOutTheSurvey, setFilledOutTheSurvey] = useState(false);
return (
<div>
{filledOutTheSurvey === true ? <AnotherComp/> : filledOutTheSurvey === false ? <Survey setFilledOutTheSurvey={setFilledOutTheSurvey} /> : null }
</div>
)
...
function Survey({setFilledOutTheSurvey}){
return <form onSubmit={() => setFilledOutTheSurvey(true)}>...</form>
}
I have created one custom component, alternative of Select component, on click shows ul and on click hide ul.
when I click I can set value to the state inside function, but i want to access the value in parent component.
so my component is
const [showMenu, setShowMenu] = useState(false);
const [value, setValue] = useState();
return (
<>
<button
className='btn'
onClick={() =>
showMenu ? setShowMenu(false) : setShowMenu(true)
}>
{props.name}
</button>
<ul className={showMenu ? "" : "hide"}>
{props.listsa.map((element) => {
return (
<li
key={element.key}
value={element.value}
onClick={(e) => {
setValue(e.target.value);
}}>
{element.label}
</li>
);
})}
</ul>
</>
I want to access value mentioned above functional component in parent component, which is my app.js
as shown below, this is return method of parent component.
<div className='App'>
{/* <Main /> */}
<OptionsComponent name='ABC Menu' listsa={abc} />
{/* here I want to use that value to perfom operations/ also on change it should show changed value */}
</div>
I tried using localStorage.setItem("value":value) it works but that will use browser memory so I am looking for alternative way.
I tried exporting variable, it shows undefined, also I tried making varibale global, it works but doesnt reflect change.
any help will be appreciated
You just need to bring the state up and pass it down, instead:
const [value, setValue] = useState();
return (
<div className='App'>
{/* <Main /> */}
<OptionsComponent name='ABC Menu' listsa={abc} value={value} setValue={setValue}/>
</div>
And
const [showMenu, setShowMenu] = useState(false);
return (
<>
<button
className='btn'
onClick={() =>
showMenu ? setShowMenu(false) : setShowMenu(true)
}>
{props.name}
</button>
<ul className={showMenu ? "" : "hide"}>
{props.listsa.map((element) => {
return (
<li
key={element.key}
value={element.value}
onClick={(e) => {
props.setValue(e.target.value);
}}>
{element.label}
</li>
);
})}
</ul>
</>
);
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 />
...
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>
);