Switch class on tabs with React.js

2019-01-23 18:47发布

So I have a tab-component that has 3 items:

React.DOM.ul( className: 'nav navbar-nav', 
    MenuItem( uid: 'home')
    MenuItem( uid: 'about')
    MenuItem( uid: 'contact)
)

And in the .render of MenuItem:

React.DOM.li( id : @props.uid, className: @activeClass, onClick: @handleClick,
    React.DOM.a( href: "#"+@props.uid, @props.uid)
)

Every time I click an item, a backbone router gets called, which will then call the tab-component, which in turn will call a page-component.

I'm still trying to wrap my head around the fact there's basically a one-way data-flow. And I'm so used to manipulating the DOM directly.

What I want to do, is add the .active class to the tab clicked, and make sure it gets removed from the inactive ones.

I know the CSS trick where you can use a data- attribute and apply different styling to the attribute that is true or false.

The backbone router already has already gotten the variable uid and calls the right page. I'm just not sure how to best toggle the classes between tabs, because only one can be active at the same time.

Now I could keep some record of which tab is and was selected, and toggle them etc. But React.js already has this record-keeping functionality.

The @handleClick you see, I don't even want to use, because the router should tell the tab-component which one to give the className: '.active' And I want to avoid jQuery, because React.js doesn't need direct DOM manipulation.

I've tried some things with @state but I know for sure there is a really elegant way to achieve this fairly simple, I think I watched some presentation or video of someone doing it.

I'm really have to get used to and change my mindset towards thinking React-ively.

Just looking for a best practice way, I could solve it in a really ugly and bulky way, but I like React.js because it's so simple.

3条回答
看我几分像从前
2楼-- · 2019-01-23 19:24

You could try to push the menu item click handler up to it's parent component. In fact I am trying to do something similar to what you are doing.. I have a top level menubar component that I want to use a menubar model to render the menu bar and items. Other components can contribute to the top level menubar by adding to the menubar model... simply adding the top level menu, the submenuitem, and click handler (which is in the component adding the menu). The top level component would then render the menubar UI and when anything is clicked, it would use the "callback" component click handler to call to. By using a menu model, I can add things like css styles for actice/mouseover/inactive, etc, as well as icons and such. The top level menubar component can then decide how to render the items, including mouse overs, clicks, etc. At least I think it can.. still working on it as I am new to ReactJS myself.

查看更多
兄弟一词,经得起流年.
3楼-- · 2019-01-23 19:35

Push the state as high up the component hierarchy as possible and work on the immutable props at all levels below. It seems to make sense to store the active tab in your tab-component and to generate the menu items off data (this.props in this case) to reduce code duplication:

Working JSFiddle of the below example + a Backbone Router: http://jsfiddle.net/ssorallen/4G46g/

var TabComponent = React.createClass({
  getDefaultProps: function() {
    return {
      menuItems: [
        {uid: 'home'},
        {uid: 'about'},
        {uid: 'contact'}
      ]
    };
  },

  getInitialState: function() {
    return {
      activeMenuItemUid: 'home'
    };
  },

  setActiveMenuItem: function(uid) {
    this.setState({activeMenuItemUid: uid});
  },

  render: function() {
    var menuItems = this.props.menuItems.map(function(menuItem) {
      return (
        MenuItem({
          active: (this.state.activeMenuItemUid === menuItem.uid),
          key: menuItem.uid,
          onSelect: this.setActiveMenuItem,
          uid: menuItem.uid
        })
      );
    }.bind(this));

    return (
      React.DOM.ul({className: 'nav navbar-nav'}, menuItems)
    );
  }
});

The MenuItem could do very little aside from append a class name and expose a click event:

var MenuItem = React.createClass({
  handleClick: function(event) {
    event.preventDefault();
    this.props.onSelect(this.props.uid);
  },
  render: function() {
    var className = this.props.active ? 'active' : null;

    return (
      React.DOM.li({className: className},
        React.DOM.a({href: "#" + this.props.uid, onClick: this.handleClick})
      )
    );
  }
});
查看更多
我命由我不由天
4楼-- · 2019-01-23 19:48

You can try react-router-active-componet - if you working with boostrap navbars.

查看更多
登录 后发表回答