Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react-bootstrap compatibility #83

Closed
flosse opened this issue Jul 17, 2014 · 18 comments
Closed

react-bootstrap compatibility #83

flosse opened this issue Jul 17, 2014 · 18 comments

Comments

@flosse
Copy link

flosse commented Jul 17, 2014

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?

@pieterv
Copy link

pieterv commented Jul 17, 2014

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>
  );

@flosse
Copy link
Author

flosse commented Jul 17, 2014

hmm...and how can I access the current active link? I need to set the active property.
Is there something like Router.getCurrent()?

@ryanflorence
Copy link
Member

related #85

@flosse
Copy link
Author

flosse commented Jul 17, 2014

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.

@stevoland
Copy link

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.

@flosse
Copy link
Author

flosse commented Jul 17, 2014

uuhh.... React.DOM.a = Router.Link; looks dangerous. What if you'd like to render a plain a element?

@stevoland
Copy link

yeah, it's totally deranged - I'm certainly not recommending it.

But <originalAnchor></originalAnchor> would work

@mjackson
Copy link
Member

8562482 adds an ActiveState mixin that lets you mixin "active" behavior to any React component.

@professor
Copy link

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>

@Silviu-Marian
Copy link

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>

@taion
Copy link
Contributor

taion commented Apr 26, 2016

Making this big so it's easier to see. The official recommendation of the maintainers of React Router and React-Bootstrap is:

Use React-Router-Bootstrap

@taion
Copy link
Contributor

taion commented Apr 26, 2016

That componentClass={Link} trick is clever, but it won't give you the right active state, the way RRB's <LinkContainer> will.

@Silviu-Marian
Copy link

Silviu-Marian commented Apr 26, 2016

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;

@taion
Copy link
Contributor

taion commented Apr 26, 2016

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".

@fengerzh
Copy link

fengerzh commented Sep 7, 2016

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>

@Slunk32
Copy link

Slunk32 commented Sep 16, 2016

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>

@fengerzh
Copy link

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;

@manonthemoon42
Copy link

manonthemoon42 commented Aug 16, 2018

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.

@lock lock bot locked as resolved and limited conversation to collaborators Oct 15, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests