how can i update the activeUrl value with setState ?
the state is
state = {
activeUrl: ''
}
the function
const handleActiveMenu = () => {
let splitUrl = window.location.href.split('/')
let assignActive = splitUrl[4]
this.setState({ activeUrl: assignActive })
}
the component
<Link
className={`${activeUrl === '' ? classes.borderBottom : null}`}
onClick={() => handleActiveMenu}
to="/"
>
Beranda
</Link>
<Link
className={`${
activeUrl === 'profil' ? classes.borderBottom : null
}`}
onClick={handleActiveMenu}
to="/profil"
>
Inisiatif
</Link>
when i trying to console.log, the activeUrl doesnt change , but after i click for 2 times, the value was change..
As far as I see, a onclick on a Link (if that is the Link from react-router-dom) may not be working. Try out a log.console or debugging if the handleActiveMenu is reached.
Additional to that, when I interpret your code correctly, you want to set the active URL after clicking the link -> You should not do that within a onClick in the Link component, try to do that within the component you called e.g. in the componentDidMount event.
Related
I'm using the new NEXTUI navbar: https://nextui.org/docs/components/navbar
I want to set isActive property on the active link and there isn't much help to get from Google so I hope someone here have used it or knows how to do so. I'm using Next.js
A portion of the code:
<Navbar.Content
enableCursorHighlight
activeColor="primary"
hideIn="xs"
variant="highlight-rounded"
>
{navigation.map((item, index) => (
<Navbar.Link key={index} href={item.link}>
{item.title}
</Navbar.Link>
))}
</Navbar.Content>
EDIT: When I add isActive in the map loop, it effects all. I want to set isActiveon the clicked (active link) one at a time. If I didn't loop my nav links (which is coming from backend) I could set IsActive property on one but then its just that one that have isActive even if I click on other links.
You have to use a specific condition in your map to check if you are on the correct route.
For example: you can use the next/router and compare it to the link property of the item.
const { asPath } = useRouter();
.
.
.
//inside return body
....
{navigation.map((item, index) => {
if(asPath === item.link)
return(<Navbar.Link isActive key={index} href={item.link}>
{item.title}
</Navbar.Link>);
});
else
return(<Navbar.Lin key={index} href=. {item.link}>
{item.title}
</Navbar.Link>);
})
}
similar to the answer above but without the if/else in the return
{navigation.map((item, index) => (
<Navbar.Link
isActive={asPath === item.link}
key={index}
href={item.link}
>
{item.title}
</Navbar.Link>);
))}
I'm a newbie to React. I'm trying to update the like or bookmark count to 0 and 1 upon button click. When I click on the bookmark icon, both the icons gets toggled. The behavior seems inconsistent.
ToggleIcon Component
const ToggleIcon = ({ icon, color, styledIcon, handleClick }: any) => {
return (
<IonIcon icon={color ? styledIcon : icon} onClick={handleClick}></IonIcon>
);
};
Root Component
{config.map((props, index) => (
<ToggleIcon
style={{ padding: "10px" }}
handleClick={() => {
setColor(!color);
if (props.type === "bookmark") {
!color && props.type === "bookmark"
? setBookmarkCount(bookmarkCount + 1)
: setBookmarkCount(bookmarkCount - 1);
}
if (props.type === "like") {
!color && props.type === "like"
? setLikeCount(likeCount + 1)
: setLikeCount(likeCount - 1);
}
}}
color={color}
{...props}
/>
))}
I created a working example using CodeSandbox. Could anyone please help?
The root cause of your problem is using a single color state to toggle the icon color. Whenever you click on any icon it triggers a change in the color state which rerenders the entire component with that color state.
I tried using multiple states for LikeColor and BookColor and it worked like a charm.Solution Link
Solution: Codesandbox
You need to separate the color into two different states. The way you have it written, one boolean value is driving the color for both the bookmark and icon color on line 27. Just because you have a loop on line 51 does not change the fact that there is only one setColor function, which you end up using twice for both <ToggleIcon/>
I suppose by now you are better in react. Just pointing out for someone else who might need this solution. Your conditional statement inside handleClick is a bit messed up
if (props.type === "bookmark") {
!color && props.type === "bookmark"
? setBookmarkCount(bookmarkCount + 1)
: setBookmarkCount(bookmarkCount - 1);
}
if (props.type === "like") {
!color && props.type === "like"
? setLikeCount(likeCount + 1)
: setLikeCount(likeCount - 1);
}
}}
color={color}
{...props}
Should include an else statement instead of 2 if(s) statements see docs below
https://reactjs.org/docs/conditional-rendering.html
I have a redux store where is rooms array stored (fetched from server) and in my component i fetch it from the store, map it and then display elements with value of room['foo'] but my problem is this: when the components are mapped and displayed the value of one that user clicks is going to be sent to server so i store clicked elements value in components local state like this:
...
handleRoomSelection(roomNumber,index,e){
this.setState({
room: roomNumber
})
}
...
{this.props.rooms.map((val,i)=>{
return (
val.reserved === false ? <p className="" key={i} onClick={e => this.handleRoomSelection(val.roomNumber,i,e)}>{val.roomNumber}</p> : null
)
})}
and this works fine, but my problem is that i want to add className "active" to active element (there can only be one active element) it would be easy if there could be many active elements (i would just add e.target.className = "active" after setState) so how can i achieve my aim?
Basically just do what you stated: set the class based on the room number in your state. setState triggers render(), so enrich even though there's nothing active on first render, once you click, render triggers and we can just set that class inside your map:
render() {
return this.props.rooms.map(val => {
if (val.reserved) return null;
return <p
className={ this.state.room === val.roomNumber && `active`}
key={val.roomNubber}
onClick={() => this.handleRoomSelection(val.roomNumber)}
>{val.roomNumber}</p>;
};
}
Note that we've changed the key to something that uniquely identifies the element we're building a <p> for: never use array index as key, because array position does not identify any specific element, it only identifies a position in a list.
Also note that there's no reason to pass more than just the room number in your click handler if the handler itself only cares about the room number.
Compare the room in state to each room's roomNumber for defining the className
{
this.props.rooms.map((val, i) => {
const className = this.state.room === val.roomNumber ? 'active' : '';
return val.reserved === false ? (
<p
className={className}
key={i}
onClick={e => this.handleRoomSelection(val.roomNumber, i, e)}
>
{val.roomNumber}
</p>
) : null;
});
}
Have you tried to use your state to populate className ?
{this.props.rooms.map((val,i)=>{
return (
val.reserved === false
? <p
className={val.roomNumber === this.state.room ? 'active' : ''}
key={i}
onClick={e => this.handleRoomSelection(val.roomNumber,i,e)}>
{val.roomNumber}
</p>
: null
)
})}
I'm building a conference website using three of these tabs (#speaker, #talks, #schedule). I think it is fair to want interactions between the tabs, here are a couple use cases that I cannot seem to solve.
From the #talks tab, I click on the bio hash - #johnsmith. This id exists within the page, but since I don't first switch tab to #speakers, nothing renders.
If I want to reference a specific talk and email someone the url: https://website.com#speaker_name the tabs won't open, and nothing but the tabs render.
The problem is compounded by the fact that when I click on an anchor tag href using a '#id', I must reload the page for it to fire.
I feel like there should be some way to pass a parameter when changing the tab or something... I'm in a tough spot because I'm rolling out code, but need this functionality badly.
Here is the actual open-source repo - https://github.com/kernelcon/website. The code I'm referencing can be found in src/pages/Agenda/.
Here is some example code.
Agenda.js
<Tabs defaultTab={this.state.defaultTab}
onChange={(tabId) => { this.changeTab(tabId) }}
vertical={vert}>
<TabList vertical>
<Tab tabFor="speakers">Speakers</Tab>
<Tab tabFor="talks">Talks</Tab>
<span>
<TabPanel tabId="speakers">
<Speakers />
</TabPanel>
<TabPanel tabId="talks">
<Talks />
</TabPanel>
</span>
</Tabs>
Talks.js
changeTab(id) {
window.location.reload(false);
}
getTalks() {
// Order Alphabetically
const talksOrdered = speakerConfig.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
const talks = talksOrdered.map((ele, idx) => {
const twitterUrl = ele.twitter.replace('#', '');
return (
<div id={ele.talk_id}
key={idx}
className='single-talk'>
<div className='talk-title'>{ele.title}</div>
<div className='talk-sub-title'>
<div className='speaker-name'>
<a onClick={() => {this.changeTab(ele.speaker_id)}}
href={`#${ele.speaker_id}`}>{ele.speaker}</a>
</div>
...
I ended up accomplishing this by sending #tab_title/speaker_name, then adding a componentWillMount lifecycle method and function in the main tab file like below.
componentWillMount() {
const defaultTab = this.props.location.hash ? this.props.location.hash.split('#')[1] : 'schedule';
this.setState({
defaultTab: defaultTab
});
this.handleHashChange();
window.addEventListener('hashchange', this.handleHashChange);
}
handleHashChange = () => {
// given `#speakers/dave` now you have tabName='speakers', speakerHash='dave'
const [tabName, speakerHash] = window.location.hash.replace('#', '').split('/');
const tabNamesToWatchFor = [
'schedule',
'speakers'
];
if (tabNamesToWatchFor.includes(tabName)) {
this.setState({
defaultTab: tabName,
// pass this.state.speakerHash to <Speakers/> and use this for scrollIntoView in componentDidMount
speakerHash: speakerHash
});
}
}
Next, I went to the individual tab (in this case Speakers.js) and added a componentDidMount and componentDidUpdate method to help scroll to the speaker itself.
componentDidMount() {
this.handleScrollToSpeaker(this.props.speakerHash);
}
componentDidUpdate(prevProps) {
if (prevProps.speakerHash !== this.props.speakerHash) {
this.handleScrollToSpeaker(this.props.speakerHash);
}
}
handleScrollToSpeaker = hash => {
window.setTimeout(() => {
const ele = document.querySelector(`#${hash}`);
if (ele) {
ele.scrollIntoView({ block: 'start', behavior: 'smooth' });
}
}, 500)
}
I am having an issue where the "right click" on the links that are created with React Router Link tags.
I want to be able to right click on those links and select "Open Link in New Tab" option.
Could anyone please help me with this issue?
Here is my code:
redirectUrl = (e) => {
let url = e.currentTarget.getAttribute("dataattrurl");
browserHistory.push({
pathname : url,
query : '',
state : {}
});
}
const listItems = readingHistory.map((item, index) => {
return (
<li key={index} role="listitem">
<Link className="linkPointer" onClick={this.redirectUrl} dataattrurl={`/document/${item.resId}`} onContextMenu={this.contextMenu}>
{item.title}
</Link>
</li>
);
});
The problem is that you are handling the click event with an onClick rather than with an href (to for Link), so the browser does not register this as a link to another page but rather an element with a click event listener.
What you should do is make use of the to attribute of Link, which is basically the href for a regular a tag. See the docs here: https://reacttraining.com/react-router/web/api/Link
This will also result in much cleaner code. All your code can be reduced to:
const listItems = readingHistory.map((item, index) => {
return (
<li key={ index } role="listitem">
<Link className="linkPointer" to={ `/document/${item.resId}` } onContextMenu={ this.contextMenu }>
{ item.title }
</Link>
</li>
);
});
So the redirectUrl can be removed entirely.