Emotion CSS-in-JS - как добавить условный CSS на основе свойств компонента?

Я хотел бы иметь компонент, оформленный с помощью Emotion, который использует свойства, которые в конечном итоге контролируют стиль. . Например, рассмотрим компонент GridCol, у которого есть различные свойства, которые изменяют заполнение и ширину (ширину можно изменять в зависимости от ширины области просмотра).

Я бы хотел использовать такой API:

<GridCol gutter size="2>

// or alternatively, like this:

<GridCol gutter size={{
  m: 2,
  l: 4
}}>

Здесь происходят три вещи:

  • gutter - это логическая опора, которая добавляет горизонтальный отступ к столбцу
  • size опора может быть строкой или объектом. Если это строка, мы просто добавляем несколько строк CSS, и у нас все хорошо, однако, если это объект, нам нужно вставить некоторые медиа-запросы на основе объекта точки останова, который установлен в другом месте.

В документации Emotion неясно, как обрабатывать такие стили, по крайней мере Я этого не видел, поэтому надеялся, что можно будет найти общее решение.

Для опоры gutter это тривиально:

const GridCol = props => styled('div')`
  display: block;
  box-sizing: border-box;
  flex: 1 0 0;
  min-width: 0;
  padding: ${props.gutter ? `0 10px` : '0'};
`

Для опоры size это становится более сложным, я бы хотел, чтобы результирующий CSS выглядел примерно так:

const GridCol = props => styled('div')`
  display: block;
  box-sizing: border-box;
  flex: 1 0 0;
  min-width: 0;
  padding: ${props.gutter ? `0 10px` : '0'};

  /* styles here if  `size` is a string */
  width: 10%;

  /* styles here if  `size` is an object */
  @media screen and (min-width: 500px) {
      width: 20%;
  }


  @media screen and (min-width: 800px) {
      width: 30%;
  }


  @media screen and (min-width: 1100px) {
      width: 40%;
  }
`

Значения width будут определяться ключом опоры, который соответствует значению в объекте breakpoints, эта часть нетривиальна, но я не знаю, как динамически генерировать необходимый css.

Я уверен, что я мог бы добавить больше информации, я предпринял несколько попыток, но ни одна из них в данный момент не работает. Мне кажется, что я должен создать функциональный компонент без сохранения состояния, который генерирует CSS для каждого условия, а затем присоединяется к CSS в конце ..


person Zander    schedule 27.11.2017    source источник


Ответы (3)


Это большой вопрос. Во-первых, избегайте этого шаблона.

const GridCol = props => styled('div')`
  display: block;
  box-sizing: border-box;
  flex: 1 0 0;
  min-width: 0;
  padding: ${props.gutter ? `0 10px` : '0'};
`

В этом примере новый стилизованный компонент создается при каждом рендеринге, что ужасно для производительности.

Любое выражение или интерполяция может быть функцией. Эта функция получит 2 аргумента: props и context

const GridCol = styled('div')`
  display: block;
  box-sizing: border-box;
  flex: 1 0 0;
  min-width: 0;
  padding: ${props => props.gutter ? `0 10px` : '0'};
`

Что касается опоры size в вашем примере, я бы использовал следующий шаблон.

import { css } from 'emotion'

const sizePartial = (props) => typeof props.size === 'string' ?
  css`width: 10%;` :
  css`
   @media screen and (min-width: 500px) {
      width: 20%;
   }


   @media screen and (min-width: 800px) {
      width: 30%;
   }


   @media screen and (min-width: 1100px) {
      width: 40%;
   }
 `

Затем вы можете использовать партиал, как любую другую функцию, которая встречается в выражении.

const GridCol = styled('div')`
  display: block;
  box-sizing: border-box;
  flex: 1 0 0;
  min-width: 0;
  padding: ${props => props.gutter ? `0 10px` : '0'};
  ${sizePartial};
`

Это чрезвычайно мощный шаблон, который можно использовать для создания многоразовых динамических стилей в вашем проекте.

Если вас интересуют другие библиотеки, использующие этот шаблон, ознакомьтесь с https://github.com/emotion-js/facepaint и https://github.com/jxnblk/styled-system

person tkh44    schedule 27.11.2017
comment
Этому ответу уже несколько лет. Верно ли это, особенно в отношении производительности? - person skilar; 10.01.2020
comment
Что касается создания нового стилизованного компонента при каждом рендеринге, да, это все еще верно. Так же, как вы не хотели бы создавать какой-либо другой компонент в функции рендеринга другого компонента, вы не хотите создавать стилизованный компонент при каждом рендеринге. - person tkh44; 24.01.2020

Если вы ищете способ реализовать условные стили с помощью Emotion css prop, вы можете сделать что-то вроде этого:

import { css } from '@emotion/core';

const styles = ({ isSelected }) => css`
  border: solid 1px black;
  border-radius: 10px;
  padding: 16px;
  cursor: pointer;
  ${isSelected === true &&
  `
    background-color: #413F42;
    color: white;
  `}
`;

const ConditionalComponent = () => {
  const [isSelected, setIsSelected] = useState(false);
  return (
    <div css={styles({ isSelected })} onClick={() => setIsSelected(!isSelected)}>
      Click here to change styles.
    </div>
  );
};

Подробнее об этом здесь: https://seanconnolly.dev/emotion-conditionals

person Sean    schedule 22.06.2020

У Emotion есть помощник под названием cx, который предоставляет функции, аналогичные популярному classnames. Вы можете использовать это для более простого написания условных выражений:

import { cx, css } from '@emotion/css'

const cls1 = css`
  font-size: 20px;
  background: green;
`

const foo = true
const bar = false

const SomeComponentWithProp = ({ foo }) => (
  <div
    className={cx(
      { [cls1]: foo === 'bar' },
    )}
  />
);

(Адаптировано из связанных документов)

person Jack Guy    schedule 30.12.2020