简体   繁体   中英

React styled-components - How to render styles based on prop condition or pseudo class?

I am trying to conditionally render a hover state / view within styled-components, leveraging the props coming in from react...

Currently, my code looks something like this:

${isHovered}, &:hover {
    background: red;
}

Unfortunately, this does no work to be able to have a true/false as I am missing something I presume to be able to do the either / or pseudo..

I want to be able to explicitly show the hover state while retaining the default pseudo hover. How can I achieve this?

The way you have your selectors right now is invalid:

Problem

${isHovered}, &:hover {
    background: red;
}

As of now, this will always translate to:

undefined, &:hover {
    background: red;
}

For simplicity, in order to interpolate properly, it must be a function accepting props that returns a string :

color: ${props => props.isHovered && "pink" };

On a related note, even if you wrapped your styled component in a higher order component where isHovered is defined as an argument, it unfortunately still won't work in production for styled-components v5 -- this worked in v4, but v5.x doesn't handle css interpolations properly within a styled component when compiled for production (see issue tracker here ).

Solutions

A better and recommended approach would be to interpolate within a CSS property:

  background: ${({ isHovered }) => isHovered && "red"};

  :hover {
    background: red;
  }

Alternatively, if you have multiple CSS rules, then you can interpolate outside of a CSS property:


  ${({ isHovered }) => isHovered && "background: red;color: white;"};

  :hover {
    background: red;
  }

Now you just would pass your component an isHovered prop.

<StyledComponent isHovered={isHovered} />

Although, technically you can do this (notice that falsey values equate to true , which may be a bug or an unhandled edge case ) ...

${({ isHovered }) => !isHovered && ".hovered"}, &:hover {
    background: red;
}

...it is NOT recommended because of how it's being interpreted:

".hovered", &:hover {
    background: red;
}

Arguably, this isn't what you'd want because .hovered isn't being used at all within the DOM, which may be confusing to other developers. Instead, it reuses the compiled hashed class name to add the CSS property within another rule (focus on the Styles tab to see the difference): screenshot

While the recommended approach sets the CSS property to the hashed class within the same rule block: screenshot


Working demo (this demo includes both example codes above, where Title uses the recommended approach and SubTitle doesn't):

编辑样式化组件 - 父级悬停

import styled, { css } from 'styled-components';

const getHoverStyle = (props) => {
   if(props?.isHovered) {
       return ( 
         css` 
            &:hover {
               background: red;
            } 
         `
       )
    }
 }
 export const SomeName = styled.div`
    ${getHoverStyle}
 `;

You can use class selector to apply style and in your styled component you can use attr call to set className attribute based on property value like:

const MyComp = styled.div.attrs((props) => ({
  className: props.isHovered ? "hovered" : ""
}))`
  color: ${(props) => props.color};
  &.hovered,
  &:hover {
    color: blue;
  }
`;

Take a look at this react sandbox .

I have created an interactive playground. You can visit the below link. It might help you.

https://codesandbox.io/s/focused-sky-vp8d9?file=/src/App.js

Below is the code.

import React, { useState } from "react";
import "./styles.css";
import styled, { css } from "styled-components";

const HoverExample = styled.div`
  background: pink;
  width: 100%;
  min-height: 80px;

  ${(props) =>
    props.isHovered &&
    css`
      &:hover {
        background: green;
      }
    `}
`;

export default function App() {
  const [isHovered, setisHovered] = useState(false);
  return (
    <div className="App">
      <button onClick={() => setisHovered(!isHovered)}>
        {!isHovered ? "Enable" : "disabled"} Hover
      </button>
      <p>Hover is {isHovered ? "enabled" : "disabled"}</p>
      <HoverExample isHovered={isHovered}></HoverExample>
    </div>
  );
}

Thanks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM