First off, I'm fairly new to React, so I'm still learning my way around.
I'm following the Introduction Article (Medium.com) on setting up using Themes with Emotion. But I'm stuck with trying to use a theme color in a const that will be used within a compose
For instance, I have:
const types = {
primary: (props) => css`color: ${props.theme.blue}`,
secondary: (props) => css`color: ${props.theme.red}`
};
const Button = withTheme(styled.button`
composes: ${props => types[props.type]};
`);
(This is a contrived example. In reality, my primary
and secondary
will have a lot more CSS.)
If I render <Button type="primary">A Button</Button>
, the color doesn't get applied. In fact, if I inspect the element, I don't even see a color
style.
However, if instead I change Button
to:
const Button = withTheme(styled.button`
composes: ${types.primary};
`);
Then I see the correct color being applied.
I'm not totally sure what I'm doing wrong here.
Just a little background:
Tagged template literals of ES2015 are template literals that can be parsed by a function by 'tagging' it with one (such as styled.button
). That function receives the template literal and all ${}
placeholders and returns the resulting string. ${}
can contain anything considered a javascript expression, e.g. a single value, function, etc.
In the case of styled
from emotion, if you pass a function into any of the placeholders, it will call that function, passing in the props of the styled
element you have used (in your example a button
) as the first argument. If you wrap the styled
template literal with a withTheme
call, that props
argument object will contain the theme prop you originally provided to the <ThemeProvider>
at your app's base component.
In your example, the reason it works for the second code block is because you are passing a function that will return a value. In the first code block you as passing a function that when called will return another function. This means the resulting style will contain a function, rather than a value.
const types = {
primary: (props) => css`color: ${props.theme.blue}`,
secondary: (props) => css`color: ${props.theme.red}`
};
const Button = withTheme(styled.button`
composes: ${props => types[props.type]};
`);
In the case of 'primary' the above evaluates to:
const Button = withTheme(styled.button`
composes: ${props => (props) => css`color: ${props.theme.blue}`};
`);
As you can see that is one level too deep. The theme will get passed in as part of props
but the second deeper function would need to be called for the css
function to then be called. In the second code block, 'primary' would evaluate to:
const Button = withTheme(styled.button`
composes: ${(props) => css`color: ${props.theme.blue}`};
`);
This would give the correct result as styled.button
will pass the props in and css
uses them within the called function directly.
Hopefully this makes some sense. This is my first stack overflow answer attempt so I am happy to improve it if it can be.