Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dropdown React & Redux

How do I get opened one, and then another. And that when you click to another area, they were closed.

React dropdown code:

<div className="header__nav">
          <div className={classnames('header__nav__title', { 'is-active' : this.props.navAuthors })} onClick={this.props.toggleNavAuthors}><FormattedMessage {...messages.authors} /></div>
          <ReactCSSTransitionGroup transitionName='header-menu-animation' transitionEnterTimeout={350} transitionLeave={false}>
            {this.props.navAuthors ? this._renderAuthors() : null}
          </ReactCSSTransitionGroup>
        </div>
        <div className="header__nav">
          <div className={classnames('header__nav__title', { 'is-active' : this.props.nav })} onClick={this.props.toggleNav}><FormattedMessage {...messages.typefaces} /></div>
          <ReactCSSTransitionGroup transitionName='header-menu-animation' transitionEnterTimeout={350} transitionLeave={false}>
            {this.props.nav ? this._renderTypefaces() : null}
          </ReactCSSTransitionGroup>
        </div>

on redux dropdown code:

import {
  SHOW_NAV,
  HIDE_NAV
} from '../constants/ActionTypes'

export function toggleNav() {
  return (dispatch, getState) => {
    const { nav } = getState()
    dispatch({
      type: nav ? HIDE_NAV : SHOW_NAV
    })
  }
}

export function hideNav() {
  return {
    type: HIDE_NAV
  }
}
like image 926
R. David Avatar asked Oct 19 '22 05:10

R. David


1 Answers

As commented state such as this which is local to the component can be kept in the component. On the other hand a requirement that a click outside the dropdown should close the dropdown (or rather all dropdowns) would again imply global state (since it's essentially a property of the page, not the dropdown anymore). So the proper redux way would be to have a reference to the currently open dropdown in your store and a click handler on your document or window that resets that dropdown. This way any additional dropdowns would also just set themselves as the open dropdown on the store automatically closing any others.

But I still prefer not over complicating my store with this kind of UI state data, so I recently created a Dropdown class that handles the "only one dropdown open at the any time" using a combination of local state and a document event handler. Here's a very simplified version of this component (also available here as a fiddle).

// Choose a unique name for your event, this will be listened to by 
// all the dropdown components.
var EVENTNAME = "dropdown-close";

// The document triggers the event whenever anything is clicked
document.addEventListener('click', (e)=>
{
    window.dispatchEvent(new CustomEvent(EVENTNAME, { 
        // need to pass in the original element as reference
        // so the handler can check if it triggered it itself
        detail: e.srcElement
    }));
});

var DropDown = React.createClass({

    getInitialState: function() 
    {
        return {open: false};
    },

    render()
    {
        let menu = null;
        if (this.state.open) { 
            menu = this.props.children;
        }
        return <div className="dropdown">
            <a className="dropdown-toggle" ref="toggle" onClick={this.toggleMenu}>Dropdown</a>
            {menu}
        </div>
    },

    toggleMenu(e)
    {
        this.setState({open: !this.state.open});
    },

    closeMenu(e)
    {
        if (e.detail !== this.refs.toggle)
        {
            this.setState({open: false});
        }
    },

    componentWillMount()
    {
        var that = this;
        window.addEventListener(EVENTNAME, this.closeMenu);
    },

    componentWillUnmount()
    {
        var that = this;
        window.removeEventListener(EVENTNAME, this.closeMenu);
    }
});

ReactDOM.render(
  <div>
    <h1>First</h1>
    <DropDown>
      <li>Item 1 (in my case these are also React comps)</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </DropDown>
    <hr/>
    <h1>Second</h1>
    <DropDown>
      <li>Item 1 (in my case these are also React comps)</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </DropDown>
  </div>,
  document.getElementById('container')
);

Essentially the dropdown renders it's children based on local state. The dropdown toggle's it's own state. Any click on the page will cause an event to be triggered that each component checks to see if it triggered the event itself, if not it will close itself.

like image 119
Alex Duggleby Avatar answered Oct 22 '22 10:10

Alex Duggleby