7

I am rendering the following Material-UI component in my app:

const handleSetActive = _spyOn => {
  linkEl.current.focus();
};

const linkEl = useRef(null);

return (
    <ListItem
      button
      component={SmoothScrollLink}
      to={cutTo}
      spy
      smooth
      offset={(phone ? -theme.mixins.toolbar.minHeightPhone : -theme.mixins.toolbar.minHeightDesktop) - 20}
      duration={500}
      onSetActive={handleSetActive}
      // className={classNames(spyOn === cutTo && classes.hover)}
      ref={linkEl}
      {...other}
    />
)

It is using the react-scroll package which fires onSetActive whenever one scrolls past that particular ListItem.

I would like, in the simplest way possible, to make ListItem (from Material-UI) enable its hover effect when handleSetActive fires.

How would I best accomplish that?

1 Answer 1

3

Here are the portions of the default styles related to the ListItem hover state:

export const styles = theme => ({
  /* Styles applied to the (normally root) `component` element. May be wrapped by a `container`. */
  root: {
    '&$selected, &$selected:hover': {
      backgroundColor: theme.palette.action.selected,
    },
  },
  /* Styles applied to the inner `component` element if `button={true}`. */
  button: {
    transition: theme.transitions.create('background-color', {
      duration: theme.transitions.duration.shortest,
    }),
    '&:hover': {
      textDecoration: 'none',
      backgroundColor: theme.palette.action.hover,
      // Reset on touch devices, it doesn't add specificity
      '@media (hover: none)': {
        backgroundColor: 'transparent',
      },
    },
  },
  /* Styles applied to the root element if `selected={true}`. */
  selected: {},
});

Since in your case you have button={true}, the styling you want can be achieved by a CSS class that applies the following:

      textDecoration: 'none',
      backgroundColor: theme.palette.action.hover,

Here's a sandbox that shows using the activeClass property for react-scroll's Link to apply a class with this styling: https://codesandbox.io/s/reactscroll-gppym.

Here's another sandbox using a ref to apply the class directly on the DOM node: https://codesandbox.io/s/reactscroll-using-ref-9w8ki; however you shouldn't use this approach (showing it for learning purposes only) since it does more work (would perform worse) than the activeClass approach and is very brittle since a re-render for other reasons could wipe out the class applied via the DOM.

Sign up to request clarification or add additional context in comments.

13 Comments

Thanks Ryan. Would you set state in handleSetActive, then use className with classNames to conditionally apply the style when depending on the state?
It felt a bit unnatural to have to create state here, when I already have a handler that fires at the right spot. Is it possible to avoid state by using a ref somehow?
Yes, you could avoid state by just toggling the class directly in the DOM with a ref for the ListItem. That will work fine so long as your app doesn’t need to know which item is active for other reasons.
Thanks Ryan. Could you help me with an example of how to do that? I am struggling
Sure. Please create a CodeSandbox with a working example of the key aspects of your setup (i.e. list items using react-scroll with onSetActive/Inactive), and I'll enhance it to show toggling the class.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.