嵌套styled components与React函数组件间transient props透传问题
styled-components中转态props在中间函数组件样式化时的透传问题
问题场景
- 有一个接收转态props的styled组件
StyledLabel; - 该组件被用于函数组件
IntermediateLabel中,组件会将接收的props传递给StyledLabel; - 用
styled()对IntermediateLabel重新样式化得到ExtraStyledLabel,但向ExtraStyledLabel传递转态props时,无法透传到IntermediateLabel;直接使用IntermediateLabel则正常。
疑问:是否无法对函数组件重新样式化?是操作错误还是bug?
示例代码
const { createRoot } = ReactDOM; const { useState, forwardRef } = React; const { createGlobalStyle } = styled; const GlobalStyle = createGlobalStyle` html, body, #app { height: 100%; min-height: 100%; } `; const App = styled.div` display: flex; flex-direction: column; justify-content: center; align-items: center; `; const StyledLabel = styled.p` color: ${(p) => (p.$isSelected ? "green" : "red")}; font-family: "Roboto Mono", monospace; font-size: 20px; `; // WORKS const IntermediateLabelStyled = styled(StyledLabel)``; // WORKS const IntermediateLabelComponent = forwardRef(({ children, ...props }) => { console.log(props.$isSelected); return <StyledLabel {...props}>{children} </StyledLabel>; }); // WORKS const IntermediateLabelForwardRef = ({ children, ...props }) => { console.log(props.$isSelected); return <StyledLabel {...props}>{children} </StyledLabel>; }; // WORKS const ExtraStyledLabelStyledComponents = styled(IntermediateLabelStyled)` font-weight: bold; font-size: 30px; `; // DOES NOT WORK const ExtraStyledLabelComponent = styled(IntermediateLabelComponent)` font-weight: bold; font-size: 30px; `; // DOES NOT WORK const ExtraStyledLabelForwardRef = styled(IntermediateLabelForwardRef)` font-weight: bold; font-size: 30px; `; const Container = () => ( <App> <GlobalStyle /> <StyledLabel $isSelected>React 18 + styled-components 6. Transient props in intermediate components</StyledLabel> <StyledLabel $isSelected>Green text is ok, red is not</StyledLabel> <ExtraStyledLabelStyledComponents $isSelected={true}>With styled component</ExtraStyledLabelStyledComponents> <ExtraStyledLabelComponent $isSelected={true}>With React component</ExtraStyledLabelComponent> <ExtraStyledLabelForwardRef $isSelected={true}>With forwardRef</ExtraStyledLabelForwardRef> <IntermediateLabelComponent $isSelected={true}>Function component directly</IntermediateLabelComponent> </App> ); const root = createRoot(document.getElementById("app")); root.render(<Container />);
原因分析
这不是bug,是styled-components的设计逻辑:
- 转态props(以
$开头)的设计目的是仅用于styled组件内部的样式计算,默认不会传递给底层DOM元素; - 当用
styled()包裹自定义函数组件时,styled-components会默认过滤所有转态props,不会把它们传递给目标函数组件,导致中间组件接收不到这些props; - 直接使用
IntermediateLabelComponent时,props是直接从父组件传递到函数组件,没有经过styled-components的过滤逻辑,所以能正常接收。
解决方案
通过withConfig方法配置shouldForwardProp,明确告诉styled-components哪些props需要透传给目标函数组件:
修改后的代码示例
// 修复:配置shouldForwardProp,允许透传以$开头的转态props const ExtraStyledLabelComponent = styled(IntermediateLabelComponent).withConfig({ shouldForwardProp: (prop) => prop.startsWith('$') })` font-weight: bold; font-size: 30px; `; // 同样修复forwardRef版本 const ExtraStyledLabelForwardRef = styled(IntermediateLabelForwardRef).withConfig({ shouldForwardProp: (prop) => prop.startsWith('$') })` font-weight: bold; font-size: 30px; `;
补充说明
shouldForwardProp接收一个函数,返回true则该props会被透传给目标组件,返回false则会被过滤;- 如果你需要同时保留默认的过滤逻辑(比如过滤掉其他用于样式的非转态props),可以结合默认判断逻辑:
shouldForwardProp: (prop, defaultValidator) => defaultValidator(prop) || prop.startsWith('$')
内容的提问来源于stack exchange,提问作者tengl
相关产品推荐
相关产品推荐

