【问题标题】:Styled components, two layers of className - How would I apply style in component declaration?样式化组件,两层 className - 如何在组件声明中应用样式?
【发布时间】:2020-12-30 10:28:37
【问题描述】:

我在 next.js 中创建了一个带有组件样式的 Container 组件。 当我在整个网站中声明使用此容器组件时,我想为其添加一个 className 并根据其使用的上下文对其进行主观样式设置。

这是我的 Container 组件的示例:

    const Container = (props) => (
    <>
        <div className="container">
            {props.children}
        </div>

        <style jsx>{`
            .container{
                width: 100%;
                max-width: 1200px;
                margin: 0 auto;
            }
        `}</style>
    </>
)

export default Container;

所以,我想给它的最大宽度为 1200 像素,并以自动边距居中。没问题,我已经做到了。

现在,我计划在我的网站页眉中使用这个组件。但在标题的情况下,我希望 Container 组件是一个 flexbox:

import Container from './Container'

const Header = (props) => (
    <>
        <header>
            <Container className="header-col">
                <div>
                    <h1>{props.title}</h1>
                </div>
                <nav>
                    <ul>
                        {/* Navigation items */}
                    </ul>
                </nav>
            </Container>
        </header>

        <style jsx>{`
            .header-col{
                display: flex;
                justify-content: space-between;
            }
        `}</style>
    </>
)

export default Header;

当我查看该站点时,我注意到我在标题中为 Container 组件指定的 flexbox 样式不存在。

我期待 className 生效,并允许我添加其他样式。

我相信这与认为 className 不是类名而是道具有关。但是,我想通过在组件上创建样式继承来保持我的代码干燥。

我该怎么做?

感谢您的帮助!

【问题讨论】:

    标签: css reactjs next.js styles


    【解决方案1】:

    这是样式化组件的工作:

    import React from "react";
    import styled from "styled-components";
    
    export default function App() {
      return (
        <>
          <Container custom={"header"}>
            <h1>Very fancy h1 with flex display</h1>
          </Container>
          <Container custom={"regular"}>
            <h1>Non-fancy h1 with no flex display</h1>
          </Container>
        </>
      );
    }
    
    const Container = styled.div`
      display: ${(props) => (props.custom === "header" ? "flex" : "block")};
      /* you can call your prop ^^^^^^ whatever you want, 
      just change it on the container element too*/
    
      & h1 {
        /* you can apply css to children like so */
    
        font-family: ${(props) =>
          props.custom === "header"
            ? '"Courier New", Courier, monospace'
            : '"Arial Black", Gadget, sans-serif'};
      }
    `;
    
    

    在上面,我创建了一个自定义样式的组件,它接收custom 属性,然后有条件地更改您要调整的值。为了使更改更直观,我还设置了字体样式,以便您可以看到两个 &lt;Container&gt; 元素之间的直接区别。


    对于更具可扩展性的解决方案(例如,针对不同主题),请使用ThemeProvider

    import React from "react";
    import styled, { ThemeProvider } from "styled-components";
    
    export default function App() {
      return (
        <>
          <ThemeProvider theme={ContainerHeader}>
            <Container>
              <h1>Very fancy h1 with flex display</h1>
            </Container>
          </ThemeProvider>
          <Container theme={"regular"}>
            <h1>Non-fancy h1 with no flex display</h1>
          </Container>
        </>
      );
    }
    
    const Container = styled.div`
      display: ${(props) => props.theme.display};
      & h1 {
        font-family: ${(props) => props.theme.h1Font};
      }
    `;
    
    Container.defaultProps = {
      theme: {
        display: "block",
        h1Font: '"Arial Black", Gadget, sans-serif'
      }
    };
    
    const ContainerHeader = {
      display: "flex",
      h1Font: '"Courier New", Courier, monospace'
    };
    
    

    代码沙盒:https://codesandbox.io/s/stack-conditional-styled-components-vmnpn?file=/src/App.js:0-773

    【讨论】:

    • 在我看来,这里有问题。这是非常明确的,因为如果我现在选择包含第三个显示选项(如网格),我将需要修改组件的逻辑。在这种规模下,这并不是什么大不了的事,但我可以想象在更大的项目中这可能会使组件逻辑看起来很丑。
    • @JackWright 查看我的更新答案,如果ThemeProviders 满足您的用例,请告诉我。
    • 哦,我以前从未使用过 Theme 提供程序... ?? 所以从某种意义上说,这会将 CSS “注入”到组件中并隐藏添加多个样式的所有逻辑?我要快速测试一下!
    • 我试过了,感觉不对。我现在混合了两种组件样式的方法,它开始看起来很乱。在 next.js 中,我喜欢您如何添加本地
    • 看来我可能需要重新考虑如何设计我的组件!真可惜。 ? 我没有给styled-components 足够的时间,所以我会调查一下,也许它会在我身上成长。谢谢您的帮助!我将把这个问题留几天,看看其他人怎么说。
    【解决方案2】:

    相信我已经找到了我自己问题的答案(我仍会将这个问题留待几天,以防它可以改进)。

    为了传递样式,您可以在样式化的 JSX 中添加“global”标志,并使用props.className 在组件中附加其他类名。

    使用props.className的父容器组件:

    const Container = (props) => (
        <>
            <div className={`container ${props.className}`}>
                {props.children}
            </div>
    
            <style jsx>{`
                .container{
                    width: 100%;
                    max-width: 1200px;
                    margin: 0 auto;
                }
            `}</style>
        </>
    )
    
    export default Container;
    

    然后,当您想使用该组件时,可以在&lt;style jsx&gt; 中使用global 标志添加其他样式:

    在标题中使用和样式更多的容器:

    import Container from './Container';
    
    const Header = (props) => (
        <>
            <header>
                <Container className="header-col">
    
                    <div>
                        <h1>{props.title}</h1>
                    </div>
    
    
                    <nav>
                        <ul>
                            <li>Hello</li>
                            <li>There</li>
                        </ul>
                    </nav>
                </Container>
            </header>
    
            <style jsx global>{`
                .header-col{
                    display: flex;
                    justify-content: space-between;
                }
            `}</style>
        </>
    )
    
    export default Header;
    

    虽然这不是 100% 完美(但在我看来它仍然相当不错):

    • 全局标志使您的样式全局化。所以其他组件可以使用 这些风格(我相信)
    • 您需要确保您的组件采用 props.className 为这种全局样式附加额外的类名

    【讨论】:

      猜你喜欢
      • 2020-01-10
      • 2019-12-12
      • 2020-10-27
      • 2019-10-13
      • 1970-01-01
      • 2021-01-12
      • 1970-01-01
      • 2021-03-24
      • 2020-10-30
      相关资源
      最近更新 更多