Wbudowane style CSS w React: jak zaimplementować a: hover?

178

Bardzo podoba mi się wbudowany wzorzec CSS w Reakcie i zdecydowałem się go użyć.

Nie możesz jednak używać :hoverselektorów i podobnych. Jaki jest więc najlepszy sposób na zaimplementowanie podświetlania po najechaniu kursorem podczas używania wbudowanych stylów CSS?

Jedną z sugestii od #reactjs jest posiadanie Clickablekomponentu i używanie go w następujący sposób:

<Clickable>
    <Link />
</Clickable>

ClickableMa hoveredstan i przekazuje je jako rekwizyty do linku. Jednak Clickable(sposób, w jaki to zaimplementowałem) zawija element Linka, divaby można go było ustawić onMouseEnteri onMouseLeavedo niego. To jednak trochę komplikuje sprawę (np. spanOpakowany divzachowuje się inaczej niż span).

Czy jest prostszy sposób?

fhucho
źródło
1
Masz całkowitą rację - jedynym sposobem na symulację: najechanie kursorem itp. Selektory ze stylami wbudowanymi jest użycie onMouseEnteri onMouseLeave. Jeśli chodzi o dokładną implementację - to zależy wyłącznie od Ciebie. Aby spojrzeć na twój konkretny przykład, dlaczego nie uczynić <Clickable/>opakowania span?
Chris Houghton,
3
sugerowałbym użycie zewnętrznych arkuszy stylów wraz z wtyczką ExtractText Webpack, to pomoże ci w dłuższej pracy, jeśli kiedykolwiek zechcesz ServerRender, w przeciwnym razie możesz wypróbować Radium github.com/FormidableLabs/radium
abhirathore2006
Obecnie Styled Component jest najlepszym rozwiązaniem do symulacji wszystkich możliwości css / scss w reakcji.
Ahmad Behzadi

Odpowiedzi:

43

Jestem w tej samej sytuacji. Naprawdę podoba mi się wzór utrzymywania stylizacji w komponentach, ale stany najechania wydają się ostatnią przeszkodą.

To, co zrobiłem, to napisanie mixinu, który możesz dodać do swojego komponentu, który potrzebuje stanów najechania. Ta mieszanka doda nową hoveredwłaściwość do stanu twojego komponentu. Zostanie ustawiony na, truejeśli użytkownik najedzie kursorem na główny węzeł DOM komponentu i ustawi go z powrotemfalse jeśli użytkownicy opuszczą element.

Teraz w funkcji renderowania komponentu możesz zrobić coś takiego:

<button style={m(
     this.styles.container,
     this.state.hovered && this.styles.hover,
)}>{this.props.children}</button>

Teraz za każdym razem stan hovered stanu ulegnie zmianie, komponent zostanie ponownie wyrejestrowany.

W tym celu utworzyłem również repozytorium piaskownicy, którego używam do samodzielnego testowania niektórych z tych wzorców. Sprawdź, jeśli chcesz zobaczyć przykład mojej realizacji.

https://github.com/Sitebase/cssinjs/tree/feature-interaction-mixin

Wim Mostmans
źródło
3
nie jest dobrym rozwiązaniem na dłuższą metę, Radium będzie lepszym wyborem lub korzystającym z zewnętrznego arkusza stylów
abhirathore2006
16
@ abhirathore2006 Radium działa w ten sam sposób, a pytanie brzmi konkretnie, jak to zrobić bez użycia zewnętrznego arkusza stylów
Charlie Martin
Czy nie miałoby sensu użycie operatora spreadu waniliowego?
PAT-O-MATION
102

Myślę, że onMouseEnter i onMouseLeave to sposoby, ale nie widzę potrzeby stosowania dodatkowego komponentu opakowania. Oto jak to zaimplementowałem:

var Link = React.createClass({
  getInitialState: function(){
    return {hover: false}
  },
  toggleHover: function(){
    this.setState({hover: !this.state.hover})
  },
  render: function() {
    var linkStyle;
    if (this.state.hover) {
      linkStyle = {backgroundColor: 'red'}
    } else {
      linkStyle = {backgroundColor: 'blue'}
    }
    return(
      <div>
        <a style={linkStyle} onMouseEnter={this.toggleHover} onMouseLeave={this.toggleHover}>Link</a>
      </div>
    )
}

Następnie możesz użyć stanu aktywowania (prawda / fałsz), aby zmienić styl linku.

Jonathan
źródło
1
Wydaje się, że to obejmuje, :hoverale nie:focus
Adam Tuttle
3
@AdamTuttle reaguje ma onFocuszdarzenie; więc możesz zrobić to samo za :focusco :hover, z wyjątkiem tego, że zamiast potrzebować onMouseEnteri onMouseLeavepotrzebujesz tylkoonFocus
Jonathan
7
Należy pamiętać, że ta metoda wymusza wykonanie w głównym wątku, podczas gdy typowe zdarzenia CSS są obsługiwane znacznie wydajniej.
Hampus Ahlgren
54

Spóźniony na imprezę, ale przyjdź z rozwiązaniem. Możesz użyć „&”, aby zdefiniować style dla n-tego elementu potomnego itp .:

day: {
    display: "flex",
    flex: "1",
    justifyContent: "center",
    alignItems: "center",
    width: "50px",
    height: "50px",
    transition: "all 0.2s",
    borderLeft: "solid 1px #cccccc",

    "&:hover": {
      background: "#efefef"
    },
    "&:last-child": {
      borderRight: "solid 1px #cccccc"
    }
},
Hitesh Sahu
źródło
1
To nie jest rozwiązanie, pytanie brzmiało jak to zrobić z INLINE css, a nie z oddzielnym arkuszem stylów.
Emmy
35
Stary, przyjrzyj się bliżej. To jest styl inline.
Jarosław Wlazło
15
To nie działa z Reactem. Potrzebujesz dodatkowej biblioteki, takiej jak komponenty ze stylami.
GG.
3
To nie działa ze stylem inline, ten przykład wprowadza zamieszanie. Jeśli to naprawdę działa, podaj lepszy przykład z pełnym komponentem.
Alexandre
2
Doskonały! Działa jak marzenie!
Fiodor
26

Możesz użyć Radium - jest to narzędzie typu open source do stylów wbudowanych w ReactJS. Dodaje dokładnie te selektory, których potrzebujesz. Bardzo popularne, sprawdź to - Radium na npm

Gyro
źródło
Właśnie natknąłem się na ten post, jak zaimplementowałbyś Radium w następującej sytuacji? module.exports = React.createClass({ displayName: 'App',})
1
@Rkhayat Możesz zawinąć ją jako module.exports = Radium(React.createClass({ displayName: 'App',}))lub przypisać klasę do wartości i dodać @Radiumdekorator nad nią, jak wspomina dokumentacja github.com/FormidableLabs/radium#usage
pbojinov
jest też taka wspaniała rzecz o nazwie CSS;)
Pixelomo,
11

Pełne wsparcie CSS jest dokładnie powodem tak ogromnej ilości bibliotek CSSinJS. Aby robić to efektywnie, musisz wygenerować rzeczywisty CSS, a nie style wbudowane. Również style inline reagują znacznie wolniej w większym systemie. Zastrzeżenie - utrzymuję JSS .

Oleg Isonen
źródło
9

Wykonane Style To - w części - z tego powodu (pozostałe to nieporozumienia z realizacji innych libs / składniowych i inline stylings brak wsparcia dla poprzedzenie wartości nieruchomości). Uwierz, że powinniśmy być w stanie po prostu pisać CSS w JavaScript i mieć w pełni niezależne komponenty HTML-CSS-JS. Dzięki ciągom szablonów ES5 / ES6 możemy teraz i może być całkiem! :)

npm install style-it --save

Składnia funkcjonalna ( JSFIDDLE )

import React from 'react';
import Style from 'style-it';

class Intro extends React.Component {
  render() {
    return Style.it(`
      .intro:hover {
        color: red;
      }
    `,
      <p className="intro">CSS-in-JS made simple -- just Style It.</p>
    );
  }
}

export default Intro;

Składnia JSX ( JSFIDDLE )

import React from 'react';
import Style from 'style-it';

class Intro extends React.Component {
  render() {
    return (
      <Style>
      {`
        .intro:hover {
          color: red;
        }
      `}

        <p className="intro">CSS-in-JS made simple -- just Style It.</p>
      </Style>
    );
  }
}

export default Intro;
Joshua Robinson
źródło
Zauważyłem w przykładzie składni JSX, że link JSFiddle ma poprawny kod, ale w pokazanym tutaj przykładzie brakuje nawiasu zamykającego po zamykającym tagu Style, a wcięcie jest wyłączone prawdopodobnie z powodu brakującego nawiasu.
bradleygsmith
8

Dodając do odpowiedzi Jonathana , oto zdarzenia, które obejmują stan skupienia i aktywny, oraz użycie onMouseOverzamiast, onMouseEnterponieważ ten ostatni nie będzie bąbelkowy, jeśli masz jakiekolwiek elementy podrzędne w celu, do którego zdarzenie jest stosowane.

var Link = React.createClass({

  getInitialState: function(){
    return {hover: false, active: false, focus: false}
  },

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

  toggleActive: function(){
    this.setState({active: !this.state.active})
  },

  toggleFocus: function(){
    this.setState({focus: !this.state.focus})
  },

  render: function() {
    var linkStyle;
    if (this.state.hover) {
      linkStyle = {backgroundColor: 'red'}
    } else if (this.state.active) {
      linkStyle = {backgroundColor: 'blue'}
    } else if (this.state.focus) {
      linkStyle = {backgroundColor: 'purple'}
    } 

    return(
      <div>
        <a style={linkStyle} 
          onMouseOver={this.toggleHover} 
          onMouseOut={this.toggleHover} 
          onMouseUp={this.toggleActive} 
          onMouseDown={this.toggleActive} 
          onFocus={this.toggleFocus}> 
          Link 
        </a>
      </div>
    )
  }
Rachel Cantor
źródło
7

Oto moje rozwiązanie wykorzystujące React Hooks. Łączy operator rozprzestrzeniania i operator trójskładnikowy.

style.js

export default {
  normal:{
    background: 'purple',
    color: '#ffffff'
  },
  hover: {
    background: 'red'
  }
}

Button.js

import React, {useState} from 'react';
import style from './style.js'

function Button(){

  const [hover, setHover] = useState(false);

  return(
    <button
      onMouseEnter={()=>{
        setHover(true);
      }}
      onMouseLeave={()=>{
        setHover(false);
      }}
      style={{
        ...style.normal,
        ...(hover ? style.hover : null)
      }}>

        MyButtonText

    </button>
  )
}
PAT-O-MATION
źródło
6

W odniesieniu do stylu-komponentów i reagować-routera v4 można to zrobić:

import {NavLink} from 'react-router-dom'

const Link = styled(NavLink)`     
  background: blue;

  &:hover {
    color: white;
  }
`

...
<Clickable><Link to="/somewhere">somewhere</Link></Clickable>
Isaac Pak
źródło
6

To może być niezły hack do umieszczania stylu w wierszu wewnątrz komponentu reagującego (a także używania funkcji: hover CSS):

...

<style>
  {`.galleryThumbnail.selected:hover{outline:2px solid #00c6af}`}
</style>

...
tomericco
źródło
5

Checkout Typestyle, jeśli używasz React with Typescript.

Poniżej znajduje się przykładowy kod dla: hover

import {style} from "typestyle";

/** convert a style object to a CSS class name */
const niceColors = style({
  transition: 'color .2s',
  color: 'blue',
  $nest: {
    '&:hover': {
      color: 'red'
    }
  }
});

<h1 className={niceColors}>Hello world</h1>
paibamboo
źródło
4

Możesz użyć modułów css jako alternatywy, a dodatkowo reagować-css-moduły do mapowania nazw klas.

W ten sposób możesz zaimportować swoje style w następujący sposób i używać normalnego CSS w zakresie lokalnym do komponentów:

import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './table.css';

class Table extends React.Component {
    render () {
        return <div styleName='table'>
            <div styleName='row'>
                <div styleName='cell'>A0</div>
                <div styleName='cell'>B0</div>
            </div>
        </div>;
    }
}

export default CSSModules(Table, styles);

Oto przykład modułów CSS pakietu webpack

svnm
źródło
FYI: Jeśli używasz Meteora, sprawdź ten pakiet: github.com/nathantreid/meteor-css-modules . Sam go używam, odnosząc do tej pory wielki sukces.
Spiralis
Jest to świetny sposób na stylizowanie komponentów reagujących, ale nie daje pełnej kontroli nad stylami wbudowanymi. Na przykład nie można zmieniać :hoverstylów w czasie wykonywania, tak jak w przypadku Radium lub innego onMouseOverrozwiązania opartego na rozwiązaniu
Charlie Martin
4

onMouseOver i onMouseLeave z setState na początku wydawało mi się trochę narzutem - ale ponieważ tak działa reakcja, wydaje mi się najłatwiejszym i najczystszym rozwiązaniem.

na przykład renderowanie strony serwera z motywami css jest również dobrym rozwiązaniem i sprawia, że ​​komponenty reagujące są bardziej czyste.

jeśli nie musisz dołączać dynamicznych stylów do elementów (na przykład do tworzenia motywów), nie powinieneś w ogóle używać stylów wbudowanych, ale zamiast tego użyj klas css.

jest to tradycyjna reguła html / css, dzięki której HTML / JSX jest czysty i prosty.

lukas gurschler
źródło
4

Prostym sposobem jest użycie operatora trójskładnikowego

var Link = React.createClass({
  getInitialState: function(){
    return {hover: false}
  },
  toggleHover: function(){
    this.setState({hover: !this.state.hover})
  },
  render: function() {
    var linkStyle;
    if (this.state.hover) {
      linkStyle = {backgroundColor: 'red'}
    } else {
      linkStyle = {backgroundColor: 'blue'}
    }
    return(
      <div>
        <a style={this.state.hover ? {"backgroundColor": 'red'}: {"backgroundColor": 'blue'}} onMouseEnter={this.toggleHover} onMouseLeave={this.toggleHover}>Link</a>
      </div>
    )
  }
Hemadri Dasari
źródło
1

Za pomocą haków:

const useFade = () => {
  const [ fade, setFade ] = useState(false);

  const onMouseEnter = () => {
    setFade(true);
  };

  const onMouseLeave = () => {
    setFade(false);
  };

  const fadeStyle = !fade ? {
    opacity: 1, transition: 'all .2s ease-in-out',
  } : {
    opacity: .5, transition: 'all .2s ease-in-out',
  };

  return { fadeStyle, onMouseEnter, onMouseLeave };
};

const ListItem = ({ style }) => {
  const { fadeStyle, ...fadeProps } = useFade();

  return (
    <Paper
      style={{...fadeStyle, ...style}}
      {...fadeProps}
    >
      {...}
    </Paper>
  );
};
Jaroslav
źródło
0

Używam do tego całkiem hakerskiego rozwiązania w jednej z moich ostatnich aplikacji, która działa do moich celów, i uważam, że jest to szybsze niż pisanie niestandardowych funkcji ustawień kursora w vanilla js (chociaż, wiem, może nie jest to najlepsza praktyka w większości środowisk ..) Więc jeśli nadal jesteś zainteresowany, proszę.

Tworzę element nadrzędny tylko po to, aby przechowywać wbudowane style javascript, a następnie dziecko z className lub id, do którego mój arkusz stylów css będzie się przyczepiał i zapisuje styl najechania w moim dedykowanym pliku css. Działa to, ponieważ bardziej szczegółowy element podrzędny otrzymuje wbudowane style js poprzez dziedziczenie, ale jego style w dymku są zastępowane przez plik css.

Zasadniczo mój rzeczywisty plik css istnieje wyłącznie w celu przechowywania efektów najechania kursorem, nic więcej. To sprawia, że ​​jest dość zwięzły i łatwy w zarządzaniu, a także pozwala mi robić ciężkie rzeczy w moich stylach komponentów React.

Oto przykład:

const styles = {
  container: {
    height: '3em',
    backgroundColor: 'white',
    display: 'flex',
    flexDirection: 'row',
    alignItems: 'stretch',
    justifyContent: 'flex-start',
    borderBottom: '1px solid gainsboro',
  },
  parent: {
    display: 'flex',
    flex: 1,
    flexDirection: 'row',
    alignItems: 'stretch',
    justifyContent: 'flex-start',
    color: 'darkgrey',
  },
  child: {
    width: '6em',
    textAlign: 'center',
    verticalAlign: 'middle',
    lineHeight: '3em',
  },
};

var NavBar = (props) => {
  const menuOptions = ['home', 'blog', 'projects', 'about'];

  return (
    <div style={styles.container}>
      <div style={styles.parent}>
        {menuOptions.map((page) => <div className={'navBarOption'} style={styles.child} key={page}>{page}</div> )}
      </div>
    </div>
  );
};


ReactDOM.render(
  <NavBar/>,
  document.getElementById('app')
);
.navBarOption:hover {
  color: black;
}
<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>

<div id="app"></div>

Zwróć uwagę, że styl wbudowany „potomny” nie ma ustawionej właściwości „kolor”. Gdyby tak było, nie zadziałałoby, ponieważ styl wbudowany miałby pierwszeństwo przed moim arkuszem stylów.

witygass
źródło
0

Nie jestem w 100% pewien, czy to jest odpowiedź, ale to sztuczka, której używam do symulacji CSS: efekt najechania kursorem z kolorami i obrazami w tekście.

`This works best with an image`

class TestHover extends React.PureComponent {
render() {
const landingImage = {     
"backgroundImage": "url(https://i.dailymail.co.uk/i/pix/2015/09/01/18/2BE1E88B00000578-3218613-image-m-5_1441127035222.jpg)",
"BackgroundColor": "Red", `this can be any color`
"minHeight": "100%",
"backgroundAttachment": "fixed",
"backgroundPosition": "center",
"backgroundRepeat": "no-repeat",
"backgroundSize": "cover", 
"opacity": "0.8", `the hove trick is here in the opcaity slightly see through gives the effect when the background color changes`
    }

  return (
    <aside className="menu">
        <div className="menu-item">
          <div style={landingImage}>SOME TEXT</div>
        </div>
    </aside>
      ); 
  }
}
ReactDOM.render(
    <TestHover />,
  document.getElementById("root")
);

CSS:

.menu {
top: 2.70em;
bottom: 0px;
width: 100%;
position: absolute;
}

.menu-item {
cursor: pointer;
height: 100%;
font-size: 2em;
line-height: 1.3em;
color: #000;
font-family: "Poppins";
font-style: italic;
font-weight: 800;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
}

Przed najechaniem

.menu-item:nth-child(1) {
color: white;
background-color: #001b37;
} 

Po najechaniu

.menu-item:nth-child(1):hover {
color: green;
background-color: white;
}

Przykład: https://codepen.io/roryfn/pen/dxyYqj?editors=0011

Rory
źródło
0
<Hoverable hoverStyle={styles.linkHover}>
  <a href="https://example.com" style={styles.link}>
    Go
  </a>
</Hoverable>

Gdzie Hoverable definiuje się jako:

function Hoverable(props) {
  const [hover, setHover] = useState(false);

  const child = Children.only(props.children);

  const onHoverChange = useCallback(
    e => {
      const name = e.type === "mouseenter" ? "onMouseEnter" : "onMouseLeave";
      setHover(!hover);
      if (child.props[name]) {
        child.props[name](e);
      }
    },
    [setHover, hover, child]
  );

  return React.cloneElement(child, {
    onMouseEnter: onHoverChange,
    onMouseLeave: onHoverChange,
    style: Object.assign({}, child.props.style, hover ? props.hoverStyle : {})
  });
}
Sig
źródło