【问题标题】: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

【问题讨论】:

    标签: reactjs react-native styled-components


    【解决方案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;
      

      【讨论】:

        猜你喜欢
        • 2018-09-02
        • 2021-10-21
        • 1970-01-01
        • 2013-06-13
        • 1970-01-01
        • 2019-12-30
        • 1970-01-01
        • 1970-01-01
        • 2018-03-06
        相关资源
        最近更新 更多