How to intercept and possibly cancel onSelect in React Bootstrap? - reactjs

I am very new to React Bootstrap and exploring how much it is customizable. While it's easy to hook into onSelect, I can't find any way to cancel the action itself. For example, if user has unsaved changes and should not go to the other route, then how onSelect can be prevented?
This is from React Bootstrap docs with a little change:
import React from 'react';
import {Navbar, Nav, NavItem, NavDropdown, MenuItem} from 'react-bootstrap';
// import './Header.scss';
function myOnSelect(eventKey, event) {
console.log(eventKey);
console.log(event);
alert(`Going ${eventKey}`);
return false // How to prevent from going there???
}
export const Header = props => {
const {brand} = props.topNav;
return (
<Navbar collapseOnSelect expand="sm" bg="light" variant="light" sticky="top"
onSelect={myOnSelect}>
<Navbar.Brand href="#home">{brand}</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav"/>
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Nav.Link href="#features">Features</Nav.Link>
<Nav.Link href="#pricing">Pricing</Nav.Link>
<NavDropdown title="Dropdown" id="collasible-nav-dropdown">
<NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item>
<NavDropdown.Item href="#action/3.2">Another action</NavDropdown.Item>
<NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item>
<NavDropdown.Divider/>
<NavDropdown.Item href="#action/3.4">Separated link</NavDropdown.Item>
</NavDropdown>
</Nav>
<Nav>
<Nav.Link href="#deets">More deets</Nav.Link>
<Nav.Link eventKey={2} href="#memes">
Dank memes
</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
);
};
Note: I do not mean going away to completely another page via a link, just another route inside the same page. It can be for "unsaved changes" or some other temporary reason, so disabling navbar menus is an overkill from UX point of view.
Extra twist to this is the project is going to use react-observable (with Redux and RxJS) for event/action logic, so it would be nice to understand how easy it is to hook React Boostrap into it. That is, some actions will need to be intercepted and handled in the epics and other middleware instead of following default React Bootstrap automation. So:
The example here can have some very specific solution, but could be nice to also hear how easy it is to do action-interceptions in other places of React Bootstrap or whether the javascript logic should be heavily customized or even abandoned.
I guess, for Navs the answer is somewhere in these arrangements: https://github.com/react-bootstrap/react-bootstrap/blob/master/src/AbstractNavItem.js
Maybe explicitly set activeKey property together with event.preventDefault(); can be used to build the desired control over nav.

We cannot stop the select from performing its onchange property. It will execute as soon as we select an option. Consider adding conditions in the myOnSelect function
function myOnSelect(eventKey, event) {
console.log(eventKey);
console.log(event);
if(condition) {
//do something
alert(`Going ${eventKey}`);
}
else {
//do something else
return false // How to prevent from going there???
}
}

Related

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.

react navbar change it to onmouseover instead of click

I wanted to load react navbar submenu on mouseover instead of click.
https://react-bootstrap.github.io/components/navbar/
import { Nav,NavDropdown,Navbar } from "react-bootstrap";
<Navbar bg="light" expand="lg">
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav>
<NavDropdown title="Beneficiary" id="basic-nav-dropdown" className="Dropdown">
<NavDropdown.Item onClick={() => this.recordList('Approved List',this.state.approvedRecords)}>Approved</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item onClick={() => this.recordList('Rejected List',this.state.rejectedRecords)}>Rejected</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item onClick={() => this.recordList('Referred Back List',this.state.refBackRecords)}>Referback</NavDropdown.Item>
</NavDropdown>
</Nav>
</Navbar.Collapse>
</Navbar>
Any help would be appreciated.
Here, I played with it for a minute
so as I first suggested we would add a local const and a function for handling dropdown visibility
const [visible, setVisible] = useState(false);
const handleHover = () => {
setVisible((prevVisible) => (prevVisible = !prevVisible));
};
And then simply
<Navbar.Collapse id="basic-navbar-nav" onMouseEnter={handleHover}>
<Nav>
<NavDropdown
title="Beneficiary"
id="basic-nav-dropdown"
className="Dropdown"
show={visible}
>
...
As I suspected you'll have to handle the mouseleave event as well, so far in my function
setVisible((prevVisible) => (prevVisible = !prevVisible));
we're toggling the visibility on mouseenter, but you get the gist :)
LMK if I can help any further, or if you come across a better solution
Well, according to their docs, that cannot be done out of the box..
You could add your own function to NavDropdown on Mouseenter.. Provided you're using react hooks it would be something like
const [isDropdownVisible, setDropdownVisible] = useState(false);
const handleHover = () => {
setDropdownVisible(true)
}
Of course you'll have to set submenu to be visible={isDropdownVisible}
and then on a parent you could just say onmouseenter={handleHover}
You might have to handle onmouseleave as well, I'm not sure.
Hope this will be a valuable guideline :)
Good luck

How to call a method from parent/another component

The issue seems to be simple: I have a main App which has a log in and log out methods, among other things:
onLoggedIn(authData) { // need to use both the user and the token
console.log(authData); // authData logged in console
this.setState({
user: authData.user.username //authData.user.username saved in userState
});
// saves token and user from handleSubmit in local storage
localStorage.setItem('token', authData.token);
localStorage.setItem('user', authData.user.username);
this.getMovies(authData.token); // retrieves films list
}
onLoggedOut() {
this.setState({
user: null
});
localStorage.clear();
}
onLoggedOut is used by a navbar button (which is also a child component):
function MyNavbar() {
return (
<Navbar bg="#781820" expand="lg">
<Navbar.Brand className="logo">
<h1>App Name</h1>
</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<Nav.Link className="text-white ml-5">Home</Nav.Link>
</Nav>
<Form inline>
<Button id="btn-logout" type="submit" onClick={() => this.onLoggedOut()}>Logout</Button>
</Form>
</Navbar.Collapse>
</Navbar>
);
}
However, despite the fact that I have tried to import the method and/or the component into the child component:
import { onLoggedOut } from '../App/App;
import { App } from '../App/App';
the onLoggedOut method doesn't work. The method works (if I place the button in the main App, it clears the items in localStorage. Any of colleagues here could possible shed some light, please? Thanks in advance.
You would need to use Context Api. I have made a gist that have a simple approach for that.
There is a simple mistake you are making
You can import a method from a class
import { onLoggedOut } from '../App/App';
import { App } from '../App/App';
I'm not sure how you are doing this import, but
If onLoggedOut is inside App, you can't do that type of import.
If onLoggedOut is outside App, it would never work, because it uses this.setState.
You need to some something that is called Lifting State Up, but for authentication, it's better to use Context Api. I have made a gist (it's kind of the same, but better for your case).

Choose item from Navbar - item stays highlited

I have simple Navbar like this:
<Navbar.Collapse>
<Nav>
<NavItem eventKey={1} href={`${process.env.PUBLIC_URL}/`}>
Blah
</NavItem>
<NavItem eventKey={1} href={`${process.env.PUBLIC_URL}/SomePage`}>
SomePage
</NavItem>
</Nav>
</Navbar.Collapse>
When I put cursor on NavItem its highlited but I would like it to be highlighted after I click on it, to inform me where on page I am.
So: to sumuarize, anyone knows how to keep selected navitem highlighted?
I think firstly you need to be using unique event key for each NavItem.
Nav has a prop activeKey and a handler onSelect, make use of this to set the activekey on Nav.
<Nav activeKey={(this.state.activeKey)}
onSelect={e => {e.preventDefault(); this.handleSelect(e);}>
After this, you will get an active prop on the clicked active item, so now its the time for some CSS pseudo selector.
.NavItem:active{
background-color: green; //some color of your choice//
}

EventKeys in NavDropdown in React-Bootstrap

I have a problem with the eventKey thing in NavDropdowns.
var Navigation = React.createClass({
handleSelect: function(eventKey){
console.log(eventKey);
},
render: function() {
return (
<Navbar brand='Navbar' toggleNavKey={0}>
<CollapsibleNav eventKey={0} onSelect={this.handleSelect}>
<Nav navbar>
<NavItem eventKey={1}>Home</NavItem>
</Nav>
<Nav navbar right hide>
<NavItem eventKey={2}>Login</NavItem>
<NavDropdown eventKey={3} title='NavDropdown' id='basic-nav-dropdown'>
<MenuItem eventKey={4}>Action 1</MenuItem>
<MenuItem eventKey={5}>Action 2</MenuItem>
</NavDropdown>
</Nav>
</CollapsibleNav>
</Navbar>
)
}
});
I want to be able in my selectHandler to tell what Nav element was clicked.
This works great for all elements except the NavDropdown:
Clicking the Dropdown does not trigger the selectHandler, which is fine.
But when I click one of the MenuItem, instead of giving me the eventKey, it gives me an event object.
How can I modify the NavDropdown so that it gives me the eventKey?
Edit: My versions are:
"react": "^0.14.0-beta3",
"react-bootstrap": "^0.25.100-react-pre.0",
It is a bug in react-bootstrap
https://github.com/react-bootstrap/react-bootstrap/issues/1268
The callback on onSelect event will receive 2 params. The first one is event obj. The second is EventKey. You can read it in doc. So if you want to get event key, you should try to call it in the second param
handleSelect: function(event,eventKey){
console.log(event)
console.log(eventKey);
},

Resources