A Button component is generally comprised of the Text element wrapped with a TouchableHighlight (or other touchable). I'm trying to create a Button component styled using styled-components, but am having trouble getting my style to respond dynamically to props.
Below, I've created a Button component similar to the Adapting based on props example found in the styled-component docs.
import React from 'react';
import { Text, TouchableHighlight } from 'react-native';
import styled from 'styled-components/native';
const colors = {
accent: '#911',
highlight: '#D22',
contrast: '#FFF',
}
const Label = styled.Text`
color: ${props => !props.outline ? colors.contrast : colors.accent};
font-weight: 700;
align-self: center;
padding: 10px;
`
const ButtonContainer = styled.TouchableHighlight`
background-color: ${props => props.outline ? colors.contrast : colors.accent};
width: 80%;
margin-top: 5px;
border-color: ${colors.accent};
border-width: 2px;
`
const Button = (props) => {
return (
<ButtonContainer
onPress={props.onPress}
underlayColor={colors.highlight}
>
<Label>
{props.children}
</Label>
</ButtonContainer>
);
};
export default Button;
After importing it, I'm using the button like this...
<Button
outline
onPress={() => console.log('pressed')}>
Press Me!
</Button>
And so, I would expect my button to look like this...
But instead it looks like this...
When I inspect using react-devtools, I can see that the outline
prop is being passed down to the Button
component.
But the prop is not passed down to any of it's children
The Passed Props part of the docs state, "styled-components pass on all their props", but I guess not all the way down?
What do I need to change so that I can dynamically style my Button based on it's props?
Here you have:
const Button = (props) => {
return (
<ButtonContainer underlayColor={colors.highlight}>
<Label>
{props.children}
</Label>
</ButtonContainer>
);
};
If ButtonContainer
was a normal React component, you wouldn't expect the props
passed to Button
to be automatically passed to ButtonContainer
. You'll have to do <ButtonContainer underlayColor={colors.highlight} {...props} />
to do it.
Actually ButtonContainer
is a normal React component, the only difference is you pre-apply some styles using an HOC.
Also if you desugar this to a React.createElement
call, you can see there's no way props
can be passed automatically, because a Function's arguments don't get passed automatically to the function calls inside it.
const Button = (props) => {
return React.createElement(ButtonContainer, { underlayColor: colors.highlight }, ...);
};
It's nothing specific to styled-components
. You just have to pass down the props yourself to ButtonContainer
, as well as to Label
.
So you'd rewrite your code to:
const Button = (props) => {
return (
<ButtonContainer underlayColor={colors.highlight} onPress={props.onPress} outline={props.outline}>
<Label outline={props.outline}>
{props.children}
</Label>
</ButtonContainer>
);
};
Technically a React component can pass down props to it's children, so ButtonContainer
could pass them down to Label
using React.Children
and React.cloneElement
APIs. But ButtonContainer
doesn't do that for obvious reasons, e.g. you'd not want underlayColor
and onPress
to be passed to Label
automatically. It would cause a lot of confusing bugs.