简体   繁体   English

Emotion CSS-in-JS - 如何基于组件道具添加条件 CSS?

[英]Emotion CSS-in-JS - how to add conditional CSS based on component props?

I'd like to have a component, styled with Emotion , that takes props that ultimately control the styling.我想要一个带有Emotion样式的组件,它采用最终控制样式的道具。 For example, consider a GridCol component that has various props that change the padding and width (the width can be changed across different viewport widths).例如,考虑一个GridCol组件,它有各种改变内边距和宽度的道具(宽度可以在不同的视口宽度之间改变)。

I'd like to use an API like this:我想使用这样的 API:

<GridCol gutter size="2>

// or alternatively, like this:

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

There are three things happening here:这里发生了三件事:

  • the gutter is a boolean prop that adds some horizontal padding to the column gutter是一个布尔道具,它向列添加一些水平填充
  • the size prop can be a string or an object. size道具可以是字符串或对象。 If it is a string, we just add a few lines of CSS and we're good, however, if it is an object, we need to insert some media-queries based on a breakpoints object that is set elsewhere.如果它是一个字符串,我们只需添加几行 CSS 就可以了,但是,如果它是一个对象,我们需要根据在别处设置的断点对象插入一些媒体查询。

Emotion's docs are not clear how to handle styling of this nature, at least I have not seen it, so I was hoping that a common solution could be found. Emotion 的文档不清楚如何处理这种性质的样式,至少我没有见过,所以我希望能找到一个通用的解决方案。

For the gutter prop, it is trivial:对于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'};
`

For the size prop, it becomes more complicated, I'd like the resultant CSS to look something like this:对于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%;
  }
`

The width values will be determined by the prop's key, which corresponds to a value in a breakpoints object, this part is not trivial, but I don't know how to dynamically generate the css needed. width值将由 prop 的 key 决定,它对应于breakpoints对象中的一个值,这部分不是微不足道的,但我不知道如何动态生成所需的 css。

I'm sure there's more info that I could add, I have made some attempts but none of them are working at the moment.我确定我可以添加更多信息,我进行了一些尝试,但目前都没有工作。 My feeling is that I should create a stateless functional component that generates the css for each condition, then joins the CSS at the end..我的感觉是我应该创建一个无状态的功能组件,为每个条件生成 css,然后在最后加入 CSS..

This is a great question.这是一个很好的问题。 First, avoid this pattern.首先,避免这种模式。

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

In this example, a new styled component is created on every render which is terrible for performance.在这个例子中,在每次渲染时都会创建一个新的样式组件,这对性能来说很糟糕。

Any expression, or interpolation, can be a function.任何表达式或插值都可以是函数。 This function will receive 2 arguments: props and context此函数将接收 2 个参数: propscontext

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

As for the size prop in your example, I would use the following pattern.至于您示例中的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%;
   }
 `

You can then use the partial just like any other function that occurs in an expression.然后,您可以像在表达式中出现的任何其他函数一样使用部分。

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

This is an extremely powerful pattern that can be used to compose reusable dynamic styles across your project.这是一个非常强大的模式,可用于在您的项目中组合可重用的动态样式。

If you are interested in other libraries that leverage this pattern check out https://github.com/emotion-js/facepaint and https://github.com/jxnblk/styled-system如果您对利用此模式的其他库感兴趣,请查看https://github.com/emotion-js/facepainthttps://github.com/jxnblk/styled-system

If you are looking for a way to implement conditional styles with Emotion's css prop, you can do something like this:如果您正在寻找一种使用 Emotion 的css道具实现条件样式的方法,您可以执行以下操作:

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

More details on this here: https://seanconnolly.dev/emotion-conditionals此处有更多详细信息: https : //seanconnolly.dev/emotion-conditionals

Emotion has a helper called cx that provides functionality similar to the popular classnames library. Emotion 有一个名为cx的帮助程序,它提供类似于流行的类名库的功能。 You can use this to write conditionals more easily:您可以使用它来更轻松地编写条件:

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' },
    )}
  />
);

(Adapted from the linked docs) (改编自链接的文档)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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