如何在样式组件之外获取主题?

Posted

技术标签:

【中文标题】如何在样式组件之外获取主题?【英文标题】:How to get the theme outside styled-components? 【发布时间】:2017-04-09 09:09:30 【问题描述】:

我知道如何从使用styled 方式创建的组件中获取theme

const StyledView = styled.View`
    color: $( theme ) => theme.color;
`;

但是如何从普通组件获取或将其应用于不同的属性?示例:

index.js

<ThemeProvider theme= color: 'red' >
    <Main />
</ThemeProvider>

main.js

<View>
    <Card aCustomColorProperty=GET COLOR FROM THEME HERE />
</View>

注意需要主题的属性怎么不叫style

【问题讨论】:

【参考方案1】:

从 v5.0 开始,您可以使用 useTheme 挂钩:

import React,  useTheme  from 'styled-components';

export function MyComponent() 
  const theme = useTheme();

  return <p style= color: theme.color >Text</p>;


你也可以使用我很久以前从 v1.2 开始贡献的 withTheme 高阶组件:

import  withTheme  from 'styled-components'

class MyComponent extends React.Component 
  render() 
    const  theme  = this.props

    console.log('Current theme: ', theme);
    // ...
  


export default withTheme(MyComponent)



下面的原始回复(忽略这个!)

虽然没有官方解决方案,但我现在想出了:

创建一个高阶组件,负责获取当前主题并作为道具传递给组件:

import React from 'react';
import  CHANNEL  from 'styled-components/lib/models/ThemeProvider';

export default Component => class extends React.Component 
  static contextTypes = 
    [CHANNEL]: React.PropTypes.func,
  ;

  state = 
    theme: undefined,
  ;

  componentWillMount() 
    const subscribe = this.context[CHANNEL];
    this.unsubscribe = subscribe(theme => 
      this.setState( theme )
    );
  

  componentWillUnmount() 
    if (typeof this.unsubscribe === 'function') this.unsubscribe();
  

  render() 
    const  theme  = this.state;

    return <Component theme=theme ...this.props />
  

然后,在你需要访问theme的组件上调用它:

import Themable from './Themable.js'
  
const Component = ( theme ) => <Card color=theme.color />

export default Themable(Component);

【讨论】:

我正在尝试在这样的功能组件上使用withTheme export const circle = withTheme((theme) =&gt; (&lt;circle fill=theme.circleColor r="40" /&gt;)) 我收到一条错误消息,说它没有返回 React 节点,但是......所以我假设 withProps 目前不适用于功能组件? 呃。错字。 “所以我假设withProps”应该是“所以我假设withTheme”。抱歉这么多 cmets - 我不小心按了第一个,无法删除它,然后由于 5 分钟计时器而不允许编辑它......等等。 @aminimalanimal 它应该可以正常工作。您的组件是否在没有 withTheme 部分的情况下工作?如果是这样,请在此处打开一个问题:github.com/styled-components/styled-components/issues【参考方案2】:

你可以使用useTheme钩子

import  useTheme  from 'styled-components';

const ExampleComponent = () => 
  const theme = useTheme();

  return (
    <View>
       <Card aCustomColorProperty=theme.color.sampleColor />
    </View>
  );
;

【讨论】:

【参考方案3】:

创建 HOC 是处理主题的好方法。让我用 React 的 Context 分享另一个想法。

上下文允许您将数据从父节点传递给它的所有子节点。 每个孩子都可以通过在组件定义中定义contextTypes 来选择访问context

假设 App.js 是您的根目录。

import themingConfig from 'config/themes';
import i18nConfig from 'config/themes';
import ChildComponent from './ChildComponent';
import AnotherChild from './AnotherChild';

class App extends React.Component 
    getChildContext() 
        return 
            theme: themingConfig,
            i18n: i18nConfig, // I am just showing another common use case of context
        
    

    render() 
          return (
              <View>
                  <ChildComponent />
                  <AnotherChild myText="hola world" />
              </View>
          );
    


App.childContextTypes = 
    theme: React.PropTypes.object,
    i18n: React.PropTypes.object
;

export default App;

现在我们的 `ChildComponent.js 想要一些主题和 i18n 字符串

class ChildComponent extends React.Component 
    render() 
        const  i18n, theme  = this.context;

        return (
           <View style=theme.textBox>
               <Text style=theme.baseText>
                   i18n.someText
               </Text>
           </View>
        );
    


ChildComponent.contextTypes = 
    theme: React.PropTypes.object,
    i18n: React.PropTypes.object
;

export default ChildComponent;

AnotherChild.js 只想要主题而不想要 i18n。他也可能是无国籍的:

const AnotherChild = (props, context) 
    const  theme  = this.context;
    return (<Text style=theme.baseText>props.myText</Text>);


AnotherChild.propTypes = 
    myText: React.PropTypes.string
;

AnotherChild.contextTypes = 
    theme: React.PropTypes.object
;

export default AnotherChild;

【讨论】:

上下文很棒,但我遇到了问题。沿树向下的组件没有收到新的上下文值,因为上下文不保证传播:如果一个组件不更新,它的所有子组件也不会更新。 我刚刚发现这篇文章解释了 Context 的问题并提供了解决方案:medium.com/@mweststrate/… 事实证明,这个解决方案是 styled-components 在内部所做的,在我的回答中,我订阅了 styled -组件上下文。看起来不错。你怎么看? @BrunoLemos 从我的角度来看,您的解决方案对我来说看起来不错!我们可能会将类似的内容添加到核心或直接公开 CHANNEL 并为此添加文档,您介意提交代码问题吗?谢谢!【参考方案4】:

要在功能组件中使用withTheme,请创建一个高阶组件。

高阶组件: 高阶组件,或 HOCs,是在以某种方式增强组件后获取组件并输出新组件的函数:const EnhancedHOCComponent = hoc(OriginalReactComponent)

功能组件中的主题示例


const MyButton = (theme) => 
const red = theme.colors.red;

return (<div  style= color: red >how are you</div>)
`

const Button =  withTheme(MyButton);
export default Button;

【讨论】:

以上是关于如何在样式组件之外获取主题?的主要内容,如果未能解决你的问题,请参考以下文章

样式化组件中的动态主题

如何在样式化组件之外设置样式?

如何在样式化组件中访问 Material-ui 的主题

如何在 next.js 中使用带有样式组件主题的 window.matchMedia?

Angular 组件如何使用由用户定义的样式表?

更改默认主题,并为表单组件添加样式