如何在不将道具传递给底层 DOM 元素的情况下扩展样式组件?

Posted

技术标签:

【中文标题】如何在不将道具传递给底层 DOM 元素的情况下扩展样式组件?【英文标题】:How to extend styled component without passing props to underlying DOM element? 【发布时间】:2018-09-24 19:58:08 【问题描述】:

我有一个样式化的组件正在扩展第三方组件:

import Resizable from 're-resizable';
...
const ResizableSC = styled(Resizable)``;
export const StyledPaneContainer = ResizableSC.extend`
    flex: 0 0 $(props) => props.someProppx;
`;


const PaneContainer = ( children, someProp ) => (
    <StyledPaneContainer
        someProp=someProp
    >
        children
    </StyledPaneContainer>
);

export default PaneContainer;

这导致浏览器控制台出现以下错误:

警告:React 无法识别 DOM 上的 someProp 属性 元素。如果您故意希望它作为自定义出现在 DOM 中 属性,将其拼写为小写someProp。如果你 不小心从父组件传递了它,将其从 DOM 中删除 元素

所以,我将 prop 更改为具有 data-* 属性名称:

import Resizable from 're-resizable';
...
const ResizableSC = styled(Resizable)``;
export const StyledPaneContainer = ResizableSC.extend`
    flex: 0 0 $(props) => props['data-s']px;
`;


const PaneContainer = ( children, someProp ) => (
    <StyledPaneContainer
        data-s=someProp
    >
        children
    </StyledPaneContainer>
);

export default PaneContainer;

这可行,但我想知道是否有一种本地方式可以在样式化组件中使用道具而不将它们传递给 DOM 元素 (?)

【问题讨论】:

【参考方案1】:

你可以试试defaultProps

import Resizable from 're-resizable';
import PropTypes from 'prop-types';
...
const ResizableSC = styled(Resizable)``;
export const StyledPaneContainer = ResizableSC.extend`
    flex: 0 0 $(props) => props.someProppx;
`;

StyledPaneContainer.defaultProps =  someProp: 1 

const PaneContainer = ( children ) => (
    <StyledPaneContainer>
        children
    </StyledPaneContainer>
);

export default PaneContainer;

我们也可以使用 'attrs' 来传递 props。这有助于附加额外的道具(示例取自 styled components 官方文档):

const Input = styled.input.attrs(
  // we can define static props
  type: 'password',

  // or we can define dynamic ones
  margin: props => props.size || '1em',
  padding: props => props.size || '1em'
)`
  color: palevioletred;
  font-size: 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;

  /* here we use the dynamically computed props */
  margin: $props => props.margin;
  padding: $props => props.padding;
`;

render(
  <div>
    <Input placeholder="A small text input" size="1em" />
    <br />
    <Input placeholder="A bigger text input" size="2em" />
  </div>
); 

【讨论】:

你能解释一下这个答案吗? 您可以使用 defaultProps 将默认道具传递给组件 StyledPaneContainer。你试过了吗? 不,我想了解如果someProp 的值需要是动态的(?),为什么会建议defaultProps 我们可以尝试使用“attrs”。更新我的答案。请检查,让我知道你的想法。 不幸的是,attrs 将所有道具传递给 DOM,因此它不会阻止 React 的无效道具警告,除非您仅使用也恰好是有效 html 属性的道具名称。我希望 styled-components 在未来改进它的 API 以更好地支持这个用例。与此同时,@tskjetne 的回答在大多数情况下都是可行的解决方法。【参考方案2】:

正如vdanchenkov 在this styled-components github issue 上所建议的那样,您可以解构道具并仅将相关道具传递给Resizable

const ResizableSC = styled(( someProp, ...rest ) => <Resizable ...rest />)`
    flex: 0 0 $(props) => props.someProppx;
`;

【讨论】:

您必须将 react 导入到写入此文件的文件中才能使其工作 我相信这也会杀死任何 as="foo" 类型的道具【参考方案3】:

对于那些(像我一样)因为 SC 和 react-router 的 Link 问题而登陆这里的人。

这与@tskjetne 的答案基本相同,但使用的是 JS 语法风格。

const StyledLink = styled(( isCurrent, ...rest ) => <Link ...rest />)(
  ( isCurrent ) => (
    borderBottomColor: isCurrent ? 'green' : 'transparent',
  ),
)

【讨论】:

我怎么没想到呢?非常感谢! 如果我们必须像这样使用它,styled-components 就不再那么有意义了。它可能是一个不错的 `styled(Link)```...`` 但不是。【参考方案4】:

2020 年更新:使用临时道具

使用release 5.1.0,您可以使用transient props。这样您就不需要额外的包装器,即减少不必要的代码:

瞬态 props 是一种新模式,用于传递仅由样式化组件显式使用的 props,并不意味着向下传递到更深的组件层。以下是您如何使用它们:

const Comp = styled.div`
  color: $props => props.$fg || 'black';
`;

render(<Comp $fg="red">I'm red!</Comp>);

注意道具上的美元符号($)前缀;这将其标记为瞬态,并且样式组件知道不将其添加到渲染的 DOM 元素或将其进一步传递到组件层次结构中。

新的答案应该是:

样式化组件:

const ResizableSC = styled(Resizable)``;
export const StyledPaneContainer = ResizableSC.extend`
    flex: 0 0 $(props) => props.$someProppx;
`;

父组件:

const PaneContainer = ( children, someProp ) => (
    <StyledPaneContainer
        $someProp=someProp  // '$' signals the transient prop        
    >
        children
    </StyledPaneContainer>
);

【讨论】:

我真的希望 styled-components 文档已更新以包含此 更新了答案中的文档 - styled-components.com/docs/api#transient-props 这应该是接受的答案。

以上是关于如何在不将道具传递给底层 DOM 元素的情况下扩展样式组件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不将图像保存在本地的情况下将捕获的图像(Surface View)传递给另一个片段?

是否可以在不将编码器传递给 json.dumps() 的情况下转储 json 中的枚举?

如何在不附加到 DOM 的情况下正确删除 html5 音频?

如何在不复制片段的情况下传递道具

在不工作之前使用样式化组件并将道具传递给伪元素

我们如何在不将 ViewController 对象推入其中的情况下将对象分配给 `UINavigationController`。