Estilos CSS embutidos no React: como implementar um: hover?

178

Eu gosto bastante do padrão CSS embutido no React e decidi usá-lo.

No entanto, você não pode usar os :hoverseletores e similares. Então, qual é a melhor maneira de implementar o realce em foco enquanto usa estilos CSS embutidos?

Uma sugestão do #reactjs é ter um Clickablecomponente e usá-lo assim:

<Clickable>
    <Link />
</Clickable>

O Clickabletem um hoveredestado e o passa como adereços para o Link. No entanto, o Clickable(a maneira que eu implementei) envolve o Linkem um divmodo que possa definir onMouseEntere onMouseLeavepara ele. Isso torna as coisas um pouco complicadas (por exemplo, spanenvolto em um divcomportamento diferentespan ).

Existe uma maneira mais simples?

fhucho
fonte
1
Você está absolutamente certo - a única maneira de simular: pairar seletores etc com estilos em linha é usar onMouseEntere onMouseLeave. Em relação à implementação exata disso - é inteiramente sua. Para examinar seu exemplo específico, por que não tornar o <Clickable/>wrapper um span?
21720 Chris Houghton
3
Gostaria de sugerir o uso de folhas de estilo externas, juntamente com ExtractText Webpack plugin, isso irá ajudá-lo no longo prazo, se um dia você desejar ServerRender caso contrário você pode tentar Radium github.com/FormidableLabs/radium
abhirathore2006
Atualmente, o Styled Component é a melhor solução para simular todas as possibilidades de css / scss em reagir.
Ahmad Behzadi

Respostas:

43

Eu estou na mesma situação. Realmente gosto do padrão de manter o estilo nos componentes, mas os estados de foco parecem ser o último obstáculo.

O que eu fiz foi escrever um mixin que você pode adicionar ao seu componente que precisa de estados de foco. Este mixin adicionará uma nova hoveredpropriedade ao estado do seu componente. Será definido como truese o usuário passar o mouse sobre o nó DOM principal do componente e configurá-lo de volta parafalse se os usuários deixarem o elemento.

Agora, na função de renderização de componentes, você pode fazer algo como:

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

Agora, sempre que o estado do hoveredestado for alterado, o componente será renderizado novamente.

Também criei um repositório de sandbox para isso, que eu mesmo uso para testar alguns desses padrões. Confira se você quiser ver um exemplo da minha implementação.

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

Wim Mostmans
fonte
3
não é uma boa solução para longo prazo, Radium será melhor escolha ou usando uma folha de estilo externa
abhirathore2006
16
@ abhirathore2006 Radium funciona da mesma maneira ea questão é especificamente como fazer isso sem usar uma folha de estilo externa
Charlie Martin
Não faria mais sentido usar um operador de propagação de baunilha?
PAT-O-MATION
102

Acho que onMouseEnter e onMouseLeave são os caminhos a seguir, mas não vejo a necessidade de um componente adicional do wrapper. Aqui está como eu o implementei:

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>
    )
}

Em seguida, você pode usar o estado de foco (verdadeiro / falso) para alterar o estilo do link.

Jonathan
fonte
1
Isto parece cobrir :hover, mas não:focus
Adam Tuttle
3
@AdamTuttle react tem um onFocusevento; assim que você poderia fazer a mesma coisa para :focusque :hover, exceto em vez de precisar onMouseEntere onMouseLeavese fosse única necessidadeonFocus
Jonathan
7
Esteja ciente de que esse método força a execução no encadeamento principal, enquanto os eventos CSS típicos são tratados com muito mais eficiência.
Hampus Ahlgren
53

Tarde para a festa, mas vem com solução. Você pode usar "&" para definir estilos para pairar enésimo filho, etc:

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
fonte
1
Esta não é uma solução, a questão era como fazê-lo com o CSS INLINE, não com uma folha de estilos separada.
Emmy
34
Cara, olha mais de perto. Este é o estilo embutido.
Jarosław Wlazło 04/02/19
15
Isso não funciona com o React. Você precisa de uma biblioteca extra, como componentes estilizados.
GG.
3
Não funciona com estilo embutido, este exemplo traz confusão. Se estiver realmente funcionando, forneça um exemplo melhor com o componente completo.
Alexandre Alexandre
2
Excelente! Funciona como um encanto!
Fyodor
26

Você pode usar o Radium - é uma ferramenta de código aberto para estilos embutidos com o ReactJS. Ele adiciona exatamente os seletores necessários. Muito popular, confira - Radium on npm

Gyro
fonte
Acabei de encontrar este post, como você implementaria o Radium na seguinte situação? module.exports = React.createClass({ displayName: 'App',})
1
@Rkhayat Você pode agrupá-lo como module.exports = Radium(React.createClass({ displayName: 'App',}))ou atribuir a classe a um valor e adicionar o @Radiumdecorador acima dele, conforme os documentos mencionam github.com/FormidableLabs/radium#usage
pbojinov
há também essa grande coisa chamada CSS;)
Pixelomo
11

O suporte total a CSS é exatamente o motivo pelo qual essa enorme quantidade de bibliotecas CSSinJS, para fazer isso com eficiência, você precisa gerar CSS real, não estilos embutidos. Também os estilos embutidos são muito mais lentos em reagir em um sistema maior. Isenção de responsabilidade - eu mantenho o JSS .

Oleg Isonen
fonte
9

Feito o estilo - em parte - por esse motivo (outros discordam da implementação de outras bibliotecas / sintaxe e estilos embutidos, falta de suporte para prefixar valores de propriedade). Acreditamos que deveríamos simplesmente escrever CSS em JavaScript e ter componentes totalmente independentes HTML-CSS-JS. Com as strings de modelo ES5 / ES6, agora podemos e também pode ser bonito! :)

npm install style-it --save

Sintaxe funcional ( 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;

Sintaxe 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
fonte
Percebi no exemplo da sintaxe JSX, o link do JSFiddle tem o código correto, mas o exemplo mostrado aqui está faltando o parêntese de fechamento após a tag Style de fechamento e o recuo está desativado provavelmente por causa do parêntese ausente.
Bradleygsmith
8

Adicionando à resposta de Jonathan , aqui estão os eventos para abranger o foco e os estados ativos, e um uso em onMouseOvervez de , uma vez onMouseEnterque o último não borbulha se você tiver algum elemento filho no destino ao qual o evento está sendo aplicado.

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
fonte
7

Aqui está a minha solução usando React Hooks. Combina o operador de propagação e o operador ternário.

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
fonte
6

Em relação aos componentes com estilo e ao react-router v4, você pode fazer o seguinte:

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

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

  &:hover {
    color: white;
  }
`

...
<Clickable><Link to="/somewhere">somewhere</Link></Clickable>
Isaac Pak
fonte
6

Isso pode ser um bom truque por ter estilo embutido dentro de um componente react (e também usar a função CSS hover):

...

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

...
tomericco
fonte
5

Estilo de checkout se você estiver usando o React with Typescript.

Abaixo está um código de exemplo para: pairar

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
fonte
4

Você pode usar módulos css como uma alternativa e, adicionalmente, react-css-modules para o mapeamento de nomes de classes.

Dessa forma, você pode importar seus estilos da seguinte maneira e usar o css normal com escopo local para seus componentes:

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);

Aqui está um exemplo de módulos webpack css

svnm
fonte
FYI: Se você estiver usando o Meteor, consulte este pacote: github.com/nathantreid/meteor-css-modules . Estou usando-o tendo grande sucesso até agora.
Spiralis
Essa é uma ótima maneira de estilizar componentes de reação, mas não lhe dá todo o controle dos estilos embutidos. Por exemplo, você não pode mudar :hoverestilos em tempo de execução como você pode com Radium ou outra onMouseOversolução baseada
Charlie Martin
4

onMouseOver e onMouseLeave com setState pareciam um pouco de sobrecarga para mim - mas como é assim que a reação funciona, parece a solução mais fácil e limpa para mim.

renderizar um servidor css com tema, por exemplo, também é uma boa solução e mantém os componentes de reação mais limpos.

se você não precisar anexar estilos dinâmicos a elementos (por exemplo, para um tema), não deverá usar estilos inline, mas sim classes css.

essa é uma regra tradicional de html / css para manter o html / JSX limpo e simples.

lukas gurschler
fonte
4

A maneira simples é usar o operador ternário

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
fonte
1

Com o uso dos ganchos:

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
fonte
0

Eu uso uma solução bastante hack-ish para isso em um dos meus aplicativos recentes que funciona para meus propósitos, e acho mais rápido do que escrever funções personalizadas de configurações de foco instantâneo no vanilla js (embora reconheço, talvez não seja a melhor prática na maioria dos ambientes) ..) Então, caso você ainda esteja interessado, aqui vai.

Eu crio um elemento pai apenas para manter os estilos javascript embutidos e, em seguida, um filho com um className ou ID no qual minha folha de estilo css se prende e grava o estilo hover no meu arquivo css dedicado. Isso funciona porque o elemento filho mais granular recebe os estilos js inline por herança, mas tem seus estilos de foco substituídos pelo arquivo css.

Então, basicamente, meu arquivo css real existe com o único objetivo de conter efeitos de foco, nada mais. Isso o torna bastante conciso e fácil de gerenciar, e me permite fazer o trabalho pesado em meus estilos de componentes React em linha.

Aqui está um exemplo:

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>

Observe que o estilo embutido "filho" não possui um conjunto de propriedades "cor". Se isso acontecesse, isso não funcionaria porque o estilo embutido teria precedência sobre minha folha de estilo.

witygass
fonte
0

Não tenho 100% de certeza se essa é a resposta, mas é o truque que uso para simular o efeito CSS: hover com cores e imagens embutidas.

`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;
}

Antes de passar o mouse

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

Em pairar

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

Exemplo: https://codepen.io/roryfn/pen/dxyYqj?editors=0011

Rory
fonte
0
<Hoverable hoverStyle={styles.linkHover}>
  <a href="https://example.com" style={styles.link}>
    Go
  </a>
</Hoverable>

Onde Hoverable é definido como:

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
fonte