16
votes

I know how to get the theme from components that are created using the styled way:

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

But how to get from normal components or apply it for different properties? Example:

index.js

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

main.js

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

Notice how the property that needs the theme is not called style

4

4 Answers

19
votes

You can use the useTheme hook since v5.0:

import React, { useTheme } from 'styled-components';

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

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

You can also use the withTheme higher order component that I contributed a long time ago since v1.2:

import { withTheme } from 'styled-components'

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

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

export default withTheme(MyComponent)



original response below (ignore this!)

While there is no official solution, I came up by now:

Create a Higher Order Component that will be responsable to get the current theme and pass as a prop to a component:

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} />
  }
}

Then, call it on the component you need to access the theme:

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

export default Themable(Component);
14
votes

You can use useTheme hook

import { useTheme } from 'styled-components';

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

  return (
    <View>
       <Card aCustomColorProperty={theme.color.sampleColor} />
    </View>
  );
};
0
votes

Creating a HOC is a good way to tackle theming. Let me share another idea using React's Context.

Context allows you to pass data from a parent node to all it’s children. Each child may choose to get access to context by defining contextTypes in the component definition.

Let's say App.js is your root.

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;

Now our `ChildComponent.js who wants some theme and i18n strings

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 who only wants theme but not i18n. He might be stateless as well:

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;
0
votes

To use withTheme in a functional component create a Higher-order-component.

Higher-order-component: higher-order components, or HOCs, are functions that take a component and output a new component after enhancing it in some manner:
const EnhancedHOCComponent = hoc(OriginalReactComponent)

Sample withTheme in a functional Component


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

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

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