Shawn
Shawn

Reputation: 5260

React menu item hover

I am trying React MobX on hover over a list item. Here is my code

export default observer( 
class SidebarContent extends React.Component {
  constructor(props) {
    super(props);
  }
  componentWillMount() {
    this.props.store.fetchSources() 
  }  

  toggleHover(){
    this.props.store.hover = !this.props.store.hover;
  }

  getArticles(src) {
    this.props.store.fetchArticles(src); 
  }

 render() {
  const style1 = this.props.style ? {...styles.sidebar, ...this.props.style} : styles.sidebar;
  const { sources, hover  } = this.props.store

    var linkStyle;
    if (this.props.store.hover) {
      linkStyle = {backgroundColor: 'black'}
    } else {
      linkStyle = {backgroundColor: 'white'}
    }

  const links = sources.map( (src,index) => (
    <a key={index} href='javascript:;' style={styles.sidebarLink} onClick={this.getArticles.bind(this, src.id)} >
          <span style={linkStyle}  onMouseEnter={this.toggleHover.bind(this)} onMouseLeave={this.toggleHover.bind(this)}     >
            <img className="ui-avaatar image" src='{ src.urlsToLogos.small }' />
            <span className="side-news-item"> {src.name} </span>  
          </span>    
    </a>
  )) 
  return (
    <MaterialTitlePanel title="Menu" style={style1}>
      <div style={styles.content}>
        <div style={styles.divider} />
        {links}
      </div>
    </MaterialTitlePanel>
  );
  }
}
);
const styles = {
  sidebar: {
    width: 256,
    height: '100%',
  },
  sidebarLink: {
    display: 'block',
    padding: '16px 0px',
    color: '#757575',
    textDecoration: 'none',
  },
  divider: {
    margin: '8px 0',
    height: 1,
    backgroundColor: '#757575',
  },
  content: {
    padding: '16px',
    height: '100%',
    backgroundColor: 'white',
  },
};

this.props.store.hover is a observable. The problem is that when mouse over one item, all of the items get the hover effect. What did I do wrong?

Upvotes: 0

Views: 1530

Answers (1)

menq
menq

Reputation: 391

Do not set Component's props directly, set it on upper Component. or you could use state feature, and always use setState() to change state.

write an subcomponent to control the Button State

code below maybe help

class SidebarContent extends React.Component {
  constructor(props) {
    super(props);
  }
  componentWillMount() {
    this.props.store.fetchSources() 
  }  

  getArticles(src) {
    this.props.store.fetchArticles(src); 
  }

 render() {


  const links = sources.map( (src,index) => <Button />); 
  return (
    <MaterialTitlePanel title="Menu" style={style1}>
      <div style={styles.content}>
        <div style={styles.divider} />
        {links}
      </div>
    </MaterialTitlePanel>
  );
  }
}

class Button extends React.Component {

  toggleHover(){
    this.setState({
      hover: !this.state.hover,
    });
  }


  render() {
    const style1 = this.props.style ? {...styles.sidebar, ...this.props.style} : styles.sidebar;
    const { sources  } = this.props.store
    const { hover } = this.state;

    var linkStyle;
    if (hover) {
      linkStyle = {backgroundColor: 'black'}
    } else {
      linkStyle = {backgroundColor: 'white'}
    }
  
    return (
        <a key={index} href='javascript:;' style={styles.sidebarLink} onClick={this.getArticles.bind(this, src.id)} >
          <span style={linkStyle}  onMouseEnter={this.toggleHover.bind(this)} onMouseLeave={this.toggleHover.bind(this)}     >
            <img className="ui-avaatar image" src='{ src.urlsToLogos.small }' />
            <span className="side-news-item"> {src.name} </span>  
          </span>    
    </a>
    );
  }

}


const styles = {
  sidebar: {
    width: 256,
    height: '100%',
  },
  sidebarLink: {
    display: 'block',
    padding: '16px 0px',
    color: '#757575',
    textDecoration: 'none',
  },
  divider: {
    margin: '8px 0',
    height: 1,
    backgroundColor: '#757575',
  },
  content: {
    padding: '16px',
    height: '100%',
    backgroundColor: 'white',
  },
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Upvotes: 2

Related Questions