I'd like to combine react-bootstrap with react-nested-router
.
e.g. defining a navbar looks like this:
var navbarInstance = (
<Navbar>
<Nav>
<NavItem key={1} href="#">Link</NavItem>
<NavItem key={2} href="#">Link</NavItem>
<DropdownButton key={3} title="Dropdown">
<MenuItem key="1">Action</MenuItem>
<MenuItem key="2">Another action</MenuItem>
<MenuItem key="3">Something else here</MenuItem>
<MenuItem divider />
<MenuItem key="4">Separated link</MenuItem>
</DropdownButton>
</Nav>
</Navbar>
);
The problem here is that NavItem
renders to an <li>
element with an <a>
tag. Do you have an Idea how I can tell the NavItem
that it should use the Link
class of react-nested-router
?
Hey @flosse, its not currently not possible to override the <a>
component within a NavItem
in react-bootstrap, but you can provide your own custom Link
like behaviour, for example:
var navbarInstance = (
<Navbar>
<Nav>
<NavItem key={1} href="#" onClick={Router.transitionTo.bind(null, 'link1')}>Link</NavItem>
<NavItem key={2} href="#" onClick={Router.transitionTo.bind(null, 'link2')}>Link</NavItem>
<DropdownButton key={3} title="Dropdown">
<MenuItem key="1" onClick={Router.transitionTo.bind(null, 'action1')}>Action</MenuItem>
</DropdownButton>
</Nav>
</Navbar>
);
hmm...and how can I access the current active link? I need to set the active
property.
Is there something like Router.getCurrent()
?
related #85
ok, here is my current hacky workaround:
module.exports = React.createClass
isActive: (name) -> window.location.hash.indexOf('#/' + name) isnt -1
onSelect: (id) -> Router.transitionTo.bind null, id
render: ->
Navbar null,
Nav null,
for l, key in links
props = { key, onSelect: @onSelect(l.id), active: @isActive(l.id) }
NavItem props, l.title
Like mentioned in #85 it would be nice to extend the API to avoid such hacks.
I think react-bootstrap should support specifying a custom link class. Would you mind opening an issue there?
As a horrible hack, would something like this work temporarily?
var originalAnchor = React.DOM.a;
Router.Link.componentConstructor.prototype.render = function () {
var props = {
href: this.getHref(),
className: this.getClassName(),
onClick: this.handleClick
};
return originalAnchor(props, this.props.children);
};
React.DOM.a = Router.Link;
Shudder.
uuhh.... React.DOM.a = Router.Link;
looks dangerous. What if you'd like to render a plain a
element?
yeah, it's totally deranged - I'm certainly not recommending it.
But <originalAnchor></originalAnchor>
would work
85624828766f3794ce42523dff645542091f95d1 adds an ActiveState
mixin that lets you mixin "active" behavior to any React component.
For the next person who googles this, I believe the current way to do this in react-router is to use
browserHistory.push(url)
Thus the code could look like
goToUrl: url => event => {
event.preventDefault();
browserHistory.push(url)
},
<NavItem eventKey={1} href="#" onClick={this.goToUrl('/profile')}>Profile</NavItem>
From NavItem.js
and SafeAnchor.js
:
import { Link } from 'react-router';
// ...
<Nav>
<NavItem componentClass={Link} href="/economies" to="/economies">Economies</NavItem>
<NavItem componentClass={Link} href="/industries" to="/industries">Industries</NavItem>
</Nav>
Making this big so it's easier to see. The official recommendation of the maintainers of React Router and React-Bootstrap is:
That componentClass={Link}
trick is clever, but it won't give you the right active state, the way RRB's <LinkContainer>
will.
Isn't it easier to just do
import React, { PropTypes } from 'react';
import { Link, IndexLink } from 'react-router';
import SafeAnchor from 'react-bootstrap/lib/SafeAnchor';
function NavLink(props, context) {
const { tagName, children, componentClass, href, to, active, index } = props;
const newProps = {
componentClass: componentClass || (index ? IndexLink : Link),
to,
href: href || to,
active: active || context.router.isActive(to),
};
const Component = tagName || SafeAnchor;
return (
<Component {...newProps}>{children}</Component>
);
}
NavLink.contextTypes = {
router: PropTypes.object,
};
NavLink.propTypes = {
tagName: PropTypes.any,
children: PropTypes.node,
componentClass: PropTypes.any,
href: PropTypes.any,
to: PropTypes.any,
active: PropTypes.bool,
index: PropTypes.bool,
};
export default NavLink;
Cloning elements is the standard API here, and is just about free outside of dev mode. RRB previously had a per-element wrappers, but the API just got unwieldy. A single container "just works".
Wrap it like this, I made huge mistake here:
<Nav>
<LinkContainer to="/link1">
<NavItem>Item 1</NavItem>
</LinkContainer>
<LinkContainer to="/link2">
<NavItem>Item 2</NavItem>
</LinkContainer>
</Nav>
I did it the same way as @fengerzh and I'm getting an ' React.createElement: type should not be null, undefined, boolean, or number. ' error. What am I doing wrong?
import { Navbar, Nav, NavItem } from 'react-bootstrap';
import LinkContainer from 'react-router-bootstrap';
<Nav>
<LinkContainer to='/view'>
<NavItem>test</NavItem>
</LinkContainer>
<NavItem eventKey={2} href="#">Link</NavItem>
</Nav>
Sorry for late reply, I did not check my email frequently.
Actually, the whole code is as below:
import React, { Component, PropTypes } from 'react';
import { Navbar, Nav, NavItem } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap';
class Main extends Component {
constructor(props) {
super(props);
this.state = {
loggedIn: 1,
};
}
render() {
return (
<div>
<Navbar>
<Navbar.Header>
<Navbar.Brand>
<a href="/">My space</a>
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
<Nav>
<LinkContainer to="/url-to-link1">
<NavItem>Item 1</NavItem>
</LinkContainer>
<LinkContainer to="/url-to-link2">
<NavItem>Item 2</NavItem>
</LinkContainer>
</Nav>
</Navbar.Collapse>
</Navbar>
<div className="content">
{this.props.children}
</div>
</div>
);
}
}
Main.propTypes = {
children: PropTypes.element.isRequired,
};
export default Main;
As recommended in upvoted comments, I tried to use the react-router-bootstrap
.
When following exactly the exact examples shared, I do get this error:
router.createHref is not a function
TypeError: router.createHref is not a function
at LinkContainer.render (/node_modules/react-router-bootstrap/lib/LinkContainer.js:126:27)
at processChild (/node_modules/react-dom/cjs/react-dom-server.node.development.js:2207:18)
It's crashing at this line of LinkContainer.js
file:
props.href = router.createHref(toLocation);
My code is pretty simple though:
import { Navbar, Nav, NavItem } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap';
const NavBarMenu = () => (
<Navbar inverse collapseOnSelect>
<Navbar.Header>
<Navbar.Toggle/>
</Navbar.Header>
<Navbar.Collapse>
<Nav>
<LinkContainer to="/">
<NavItem>Home</NavItem>
</LinkContainer>
<LinkContainer to="/news">
<NavItem>News</NavItem>
</LinkContainer>
<LinkContainer to="/news">
<NavItem>Bio</NavItem>
</LinkContainer>
</Nav>
</Navbar.Collapse>
</Navbar>
)
The package versions Im using:
"antd": "^3.8.1",
"express": "^4.16.3",
"next": "^6.1.1",
"react": "^16.4.2",
"react-bootstrap": "^0.32.1",
"react-dom": "^16.0.0",
"react-router-bootstrap": "^0.23.3",
"react-router-dom": "^4.3.1",
"styled-jsx": "^3.0.2"
Any ideas guys? @taion @fengerzh ?
Im pulling my hair since im facing this error.
Most helpful comment
From
NavItem.js
andSafeAnchor.js
: