Whats the best way to handle Nav item selection? - reactjs

I'm using React + Redux for one of my applications. Where I want to have a Nav, with 2 Nav Items. I want to handle the change of Nav item based on the user action.
I'm planning to use my redux store to have a state for this to handle this change. Is this a good approach or please guide me for a better solution. I'm a beginner in this.
My Nav will look something like this
<Nav {...props} activeKey={active} onSelect={onSelect} style={styles} appearance="subtle" justified>
<Nav.Item eventKey="nav-1">
Nav 1
</Nav.Item>
<Nav.Item eventKey="nav-2" >
Nav 2
</Nav.Item>
</Nav>
(using rsuite js for this)
My initial state of the store will be something like this
const initialState = {
selectedNav: 'nav-1'
};

yes I think you are on the right way, but Nav 1 and Nav 2 does not have to know about the state the parent Component does
pass the redux state as props to the parent Nav and then render the selected Nav from the element props.
<Nav onSelect={onSelect} style={styles} appearance="subtle" justified>
{props.selectedNav === 'nav-1' && (
<Nav.Item eventKey="nav-1">
Nav 1
</Nav.Item>
)}
{props.selectedNav === 'nav-2' && (
<Nav.Item eventKey="nav-2" >
Nav 2
</Nav.Item>
)}
</Nav>
and make a global action for the user interaction to change the active nave.
Note:
you can take another approach for more cleaner code by wrapping the Nav 1 and Nav 2 into a switch component and render the active one.

Related

How to get current hash route in single page website

I have a single-page web portfolio in which I have a navbar like the following code:
const Nav = ({ isToggled, onToggle }) => {
const [activeNav, setActiveNav] = useState('#')
return (
<nav>
<a
href='/#'
onClick={() => setActiveNav('#')}
className={activeNav === '#' ? 'active' : ''}
>
<AiOutlineHome />
</a>
<a
href='#about'
onClick={() => setActiveNav('#about')}
className={activeNav === '#about' ? 'active' : ''}
>
<AiOutlineUser />
...
</nav>
)
}
I have hyperlink tags to navigate through the website. However, I would like to update the active nav icon according to the current place the user is in. I found something called useLocation() from react-router-dom, but I currently do not use this package. Is it necessary? What should I do to achieve my goal? Thanks in advance for any help you can provide.
A really simple solution I could think of would be combining the use ScrollTrigger plugin from GSAP and React useEffect, they have really great documentations on their site! But this is the most basic example, say if you just want the nav link to show an active state if the section enters the viewport
useEffect(() => {
gsap.to(".nav-link-className", {
scrollTrigger: "#id",
// active state styling here
});
})
EDIT:
Since I do not use GSAP myself that often, there may be a much better way of going about this within GSAP, but here is an example on CodeSandBox, this should at least get you started. Note that one limitation (or at least my own limitation due to lack of knowledge with GSAP) is that I would have to hard code the animation when the trigger element leaves the DOM
The solution I found was to use the react-scroll package. I just switched my a tags for Link from react-scroll.
This:
<a
href='/#'
onClick={() => setActiveNav('#')}
className={activeNav === '#' ? 'active' : ''}
>
<AiOutlineHome />
</a>
For this:
<Link
activeClass='active'
to={'header'}
spy={true}
duration={500}
offset={-100}
>
<AiOutlineHome />
</Link>
In App.jsx, wrapped each component in Element from react-scroll (e.g. <Element name={'portfolio'}><About /></Element>).

Warning: validateDOMNesting(…): <a> cannot appear as a descendant of <a>

I'm using React router dom Link component. It is basically twitter's home feed. I want to be able to have two type of Links in one div component. One will be Link to go to user's profile and other one to go to post. I am currently getting warning and couldn't find solution for now. Here is the screenshot as reference:
I understand the issue here, my post Link is the parent element and I've added two user Link components inside of it as the user should be able to access post page when he clicks on anything inside of the post except user's profile photo and user's name. Is there any smarter way of achieving this and keeping links like this?
Code:
{posts?.map((post) => (
<Link
className={classes.link}
key={post.user.id}
to={`/posts/${post.id}`}
>
<div className={classes.post}>
<Link to={`/users/${post.user.id}`}>
<img
className={classes.profileImage}
src={DefaultLogo}
alt="default-profile"
/>
</Link>
<article className={classes.postDetails}>
<Link
className={classes.link}
to={`/users/${post.user.id}`}
>
<Typography
variant="subtitle1"
className={`${classes.postTitle} ${classes.content}`}
>
{post.user.name}
</Typography>
</Link>
<Typography
variant="subtitle1"
className={`${classes.postText} ${classes.content}`}
>
{post.body}
</Typography>
</article>
</div>
</Link>
))}
Yes, having anchor tags inside of another anchor tag is misleading a bad approach to doing things. But given your requirements you can make use of a basic button with react router dom history api.
A simple example:
import {Link, useHistory} from 'react-router-dom'
const App = () => {
const history = useHistory()
return (
<a
href='/user/1'
>
<h2>John doe</h2>
<div>here are some use information</div>
{/* Need to prevent the event bubbling */}
<Link role='link' to='/users/1/posts'>
User posts
</Link>
</div>
)
}

How to display React-Bootstrap's NavDropdown on Hover

Related to this question, whose top answers no longer work. By default, the NavDropdown only appears when clicked on, however I need this dropdown to display on hover. I struggled loading 'React-Bootstrap' into stackoverflow to create a reproducible example, however here is a basic Navbar using react-bootstrap, that features my attempt to display the dropdown on hover:
const [isStatsOpen, setIsStatsOpen] = useState(true);
<Navbar>
<Navbar.Brand>
<Link to='/'>
<img alt='company logo' src={My Logo} />
</Link>
</Navbar.Brand>
<Navbar.Toggle aria-controls='basic-navbar-nav' />
<Navbar.Collapse id='basic-navbar-nav'>
<Nav className='mr-auto'>
<NavDropdown title='Statistics'
onMouseEnter={() => setIsStatsOpen(true)}
onMouseLeave={() => setIsStatsOpen(false)}
open={isStatsOpen}
>
<NavDropdown.Item as={Link} to='/stats/'> Stats 1</NavDropdown.Item>
<NavDropdown.Item as={Link} to='/stats2/'>Stats 2</NavDropdown.Item>
</NavDropdown>
</Nav>
<Nav className='ml-auto'>
<DivisionSelect />
<AppSelect />
</Nav>
</Navbar.Collapse >
</Navbar >
From the linked post above, there were 2 responses with 10+ votes, however neither of these solutions work. As it pointed out in one of the comments: This doesn't work in newer versions, the dropdown isn't rendered until it's first click. You'd need to trigger the onclick before you could control via css.
After inspecting the page, I can confirm that this person is correct - there is no menu for which to display until after the NavDropdown has been clicked upon. Once clicked, the menu is there, and then the solutions from this other post do work. Given this as the case, how can I resolve this issue? Is it possible for my react component to automatically "click" the Navdropdowns on load, that way the menus will appear on hover?
Thanks!
Does this help you? Old good vanilla javascript.
I added an id at NavDropdown and called the old, classic document.getElementById method and then triggered the click event.
useEffect(() => {
document.getElementById("test").click();
}, []);
<NavDropdown
id="test"
title="Statistics"
https://codesandbox.io/s/react-bootstrap-demo-z36c5
In this link to the earlier version of the question, the highly voted answer that starts with export class Nav extends React.Component { does work, so long as the open prop is updated to show.

Semantic UI React side bar render pusher only on visibility change (redux/rematch)

I am using react and semantic. I am using the multiple sidebar example. The idea is that the left hand sidebar offers up some menu options, and then the right hand sidebar is the sub menu based on which option from the left menu is chosen. When a sub menu item is selected, a component is added to the Sidebar.Pusher, i.e displayed on the page.
It all works except re-rendering the content of the Sidebar.Pusher. This apparently only updates when the left hand side bar's visibility changes. I am using redux/rematch to handle state, and can see that the state that holds the content of the Sidebar.Pusher is being updated, but `render() is only being called when visibility changes of the sidebar.
The content of Sidebar.Pusher is an array, and I even tried displaying on the page the length of the array, which is being updated (pushed into) each time an item on the right hand sidebar is clicked. However this doesn't cause a render() to be fired, its literally when the left hand sidebar visibility changes.
Just to note, I did see this issue, however its from last year, and the answer wasn't enough for me to be able to fix the issue. Help would be appreciated.
Structure:
Index.js renders App.js, App.js renders Menu.js (which is a semantic set of tabs). One of the menu options is Sidebar.js which renders:
<Sidebar.Pushable as={Segment}>
<Sidebar
as={Menu}
animation="overlay"
direction="right"
inverted
vertical
visible={secondaryVisibility}
width="wide"
>
{focusedList.map((el, i) => {
return (
<Menu.Item key={i} as="a" onClick={() => this.addSegment(el)}>
<Article el={el} />
</Menu.Item>
)
})}
</Sidebar>
<Sidebar
as={Menu}
animation="overlay"
icon="labeled"
inverted
// onHide={this.handleSidebarHide}
vertical
visible={primaryVisibility}
width="wide"
>
<Menu.Item
onClick={() => this.changeTab(menuItem)}
as="a"
name="menuItem"
header
>
Menu Item
</Menu.Item>
</Sidebar>
<Sidebar.Pusher style={{ minHeight: "600px" }}>
<Segment basic>
{segments.map((el, i) => {
console.log(`el ${el}`)
return <Content key={i} segment={el} />
})}
</Segment>
</Sidebar.Pusher>
and all state (secondaryVisibility etc) is stored in rematch
Thanks
I haven't been able to identify the problem based on the code you've posted, could you provide more info such as the entire Sidebar.js and maybe what's in the Content component?. My guess would be that there's a HOC or lifecycle method getting in the way.
I've created a trivial example that seems to work fine, if I understand what you're trying to accomplish: https://codesandbox.io/s/myl6xpz9py
I got it. I forgot about immutability in state. Perhaps someone will benefit from this.
I was trying to update a state array with
let tmp = prevState.contract.segments
tmp.push(segment)
this.update({ segments: tmp })
However, this won't work as tmp is a reference to prevState.contract.segments, so this won't work, as pushing to tmp is equivelent to pushing to prevState.contract.segments.
you have to have a completely new array:
const tmp = [...prevState.contract.segments, segment]
this.update({ segments: tmp })
Now it works.

React pass children to sibling

I have run into a situation where it seems I need to pass the children of one component over to another that is a sibling of the original. This doesn't sit well with me as it seems to go against what I've learned is the proper hierarchy and communications channels in React. I need the components to render as siblings due to styling issues, but I'd like to have a logical grouping of components for developers utilising my component.
In short I have some buttons and some of these buttons display a drop-down menu. Like a toolbar of sorts. I want the menu to pop up in a modal like way and so I don't want to place a sub-div next to the button. Instead I want it to render below the buttons and just swap its components with whatever menu is currently active.
Here is how I envision the rendering to look:
<ul>
<li><button>Triggers some action elsewhere</button></li>
<li><button>Button with menu</button></li>
<li>Link</li>
<li><button>Button with menu</button></li>
<li><button>Triggers some action elsewhere</button></li>
</ul>
<div class="menu-container">
Menu content goes here
</div>
Since the buttons are 1-1 linked with a menu I want the developer to work with the components like this:
<Nav>
<NavButton text="Triggers some action elsewhere"/>
<NavMenu text="Button with menu">
<Link to="/">A link</Link>
<Box>
...
</Box>
</NavMenu>
<NavLink>
<Link to="/">Link</Link>
</NavLink>
<NavMenu text="Button with menu">
<Link to="/">A link</Link>
<Box>
...
</Box>
</NavMenu>
<NavButton text="Triggers some action elsewhere"/>
{/*
This component is dynamically inserted by <Nav/> at this position.
It is responsible for rendering a floating menu and controlling its
behaviour.
*/}
<FloatingMenu></FloatingMenu>
</Nav>
The <Nav> component would then swap the content of the .menu-container div based on whether a menu was open or not.
EDIT!
I forgot to mention that there is a separate component here that the developer doesn't see. The <FloatingMenu> component is dynamically inserted by <Nav> and not actually part of the set of children that the developer will write. It is responsible for rendering the actual menu div and controlling its behaviour. So what I'd like to do is move all children from the currently active <NavMenu/> component into the <FloatingMenu/> and empty it out if no menus are active. At least that was my original idea.
/EDIT!
Can this be done? Am I over complicating or misunderstanding something? Any help would be much appreciated!

Resources