As an example, let's say I've a component that can take in props like this:
const testComponent = (props: {isBold: boolean}) => {
if(props.isBold)
return <strong><div>hello</div></strong>
return <div>hello</div>
}
In this case, my example component that can take in props and the result depends on the props given to it.
Now, if I extend this component in styled-components, how can I pass my props into the base component? The idea is something like this:
const styledTestComponent = styled(testComponent({isBold: true}))`
width: 100%;
opacity: 0.5
/* etc etc... */
`
Well, obviously not going to work. This part will fail: styled(testComponent({isBold: true}))
But the idea is that what I want to do is to use CSS to style a particular instance of a component. So in that case, I will need to pass a pre-defined props to the base component, testComponent.
How can I achieve this?
Update:
I've come up with a quick example to illustrate the issue. The code below attempts to style a react component MyCustomImage as a styled-component StyledMyCustomImage. When this is run, you can see that StyledMyCustomImage does render itself as MyCustomImage. However, the CSS styles are not applied.
const MyCustomImage = props => (
<img
src={`https://dummyimage.com/${props.width}x${props.height}/619639/000000`}
/>
);
const StyledMyCustomImage = styled(MyCustomImage)`
border: 2px dotted red;
`;
function App() {
return (
<div className="App">
<h3>Test passing props from styled component to base component</h3>
<StyledMyCustomImage width="600" height="400" />
</div>
);
}
I've created a sandbox for this demo: https://codesandbox.io/s/k21462vjr5
Update 2:
Oh! Thanks to #SteveHolgado's answer, I've gotten it to work! I didn't know styled component will pass the CSS as a prop to its base component! Here's the code after adding in the class name for future reference:
const MyCustomImage = props => (
<img
src={`https://dummyimage.com/${props.width}x${props.height}/619639/000000`}
className={props.className}
/>
);
const StyledMyCustomImage = styled(MyCustomImage)`
border: 2px dotted red;
`;
The sadnbox of the working demo: https://codesandbox.io/s/j4mk0n8xkw
Try this, it should work
const StyledTestComponent = styled(testComponent)`
width: 100%;
opacity: 0.5
/* etc etc... */
`
and pass the prop to instance in this way.
<StyledTestComponent isBold />
Feedbacks are welcome. I have not checked it working, but feels it will work
Note: I checked and it's working. Should work for you.
When you use the styled function like that, your wrapped component will get passed a prop called className, which you need to apply to the element that you want the styles to affect:
const testComponent = (props) => {
return <div className={props.className}>hello</div>
}
You will have access to all props in your styles, which you can use like this:
const styledTestComponent = styled(testComponent)`
width: 100%;
opacity: 0.5;
font-weight: ${props => props.isBold ? "bold" : "normal"};
/* etc etc... */
`
Related
Is there a way to create a dynamic tag using styled-components?
For example:
const MyComponent = styled(CustomTag)``;
<MyComponent element="h1" />
You can use as prop by default on components created with styled-components. If in your example CustomTag is also a styled-component that styles a native element (e.g.:)
const CustomTag = styled.h1`
color: red;
`;
then you can do
const MyComponent = styled(CustomTag)`
font-size: 64px;
`;
<MyComponent as="span">Something</MyComponent>
and you'll end up with a <span> tag with font-size of 64px and red text color. Of course you can also use as prop on CustomTag so you don't necessarily need MyComponent.
Maybe this will help you:
Add you code in your typescript
class Test extends HTMLElement {
connectedCallback() {
this.innerHTML = `<h1>Hello World...</h1>`;
this.style.color = "red";
}
}
customElements.define('test', Test);
after compiling refer the js file in you HTML and you can use it like <test></test>
I want to use a useState hook to change the color of my react icons to blue in my sidebar upon clicking one of them. I tried this
const [changeColor, setChangeColor] = useState('blue');
and then in the return
<IconOuter onClick={() => setChangeColor(changeColor)}>
{item.icon}
I would like to know what I am doing wrong? Any help would be greatly appreciated. Thank you.
Upon further inspection, I styled using component styles, so this is my css for the icon. It looks like theres a span surounding the icons which may be easier to style.
const IconOuter = styled.span`
background-color: white;
border-radius: 5px;
padding: 10px;
width: 44px;
height: 44px;
left: 8px;
top: 8px;
`;
When using the useState hook you create a variable and a method, the variable is used to store the state and the method to change the value of the variable. The variables initial value is gained from the value inside the useState hook and you can change that value latter by using the method you defined from the useState hook
This is the basic form of the useState hook:
const [state, setState] = UseState(<initial state>)
So your code should be :
const [myColor, setmyColor] = useState('white'); //the color is currently white
<IconOuter onClick={() => setColor('blue')} />
const IconOuter = styled.span`
background-color: ${ myColor };
border-radius: 5px;
padding: 10px;
width: 44px;
height: 44px;
left: 8px;
top: 8px;
`;
I can see that the default value for this state is blue and in your code, you just call setChangeColor and pass 'blue' again so if you click on it again and aging, still state is blue since you just pass 'blue' to your changeColor state method(setChangeColor()).
So I can just see that the state gets the same value as the default value all the time.
You haven't put the rest of the code but in this small piece of code that you have shared here, I can see that you are not using this state value anywhere.
You can try inline CSS as
<IconOuter onClick={()=>setChangeColor('blue')} style={{color:changeColor}}/>
If you want component style try using color instead of background-color in CSS.
Here is an example that can be helpful. In this case, I want to update the colour of my heart IonIcon when a user clicks on it once, so I first wrapped it in touchableOpacity to get that blink effect. If you don't want to wrap it, you simply put the onPress function inside the icon.
function MyComponent= () => {
const [defaultcolor, setNewColor] = useState('white')
return (
<View>
<TouchableOpacity style={styles.MyRoundButtonStyle} onPress={() =>
setColor('green')} >
<Ionicons name="heart" size={50} color={defaultcolor} />
</TouchableOpacity>
</View>
)};
export default MyComponent;
Note that in the IonIcons I did not make color='green' etc, I rather used the name of the initial state of the state hook (defaultcolor).
Note that it is just a name that you can set to anything like 'mycolor' etc. You may be familiar with the naming convention that is something like [state, setState], it is just a choice.
Do let me know if you need more clarity with this.
const Component = () => <CustomButton color="highlight">Click me</CustomButton>;
const colors = { highlight: "#123456" };
export const CustomButton = styled(Button)`
${({ props }) =>
color: ${colors[props.color]}};
`;
How can I prevent React form rendering "color="highlight" as an inline style in line 1?
I sometimes use CSS named properties to use them within my CSS in JS library as props (styled components in this case).
React renders this HTML, though:
color="highlight" is not valid HTML and displays no color.
Since color="highlight is rendered as an inline style, my styled components stylesheets are not working anymore and the styles are broken.
The correct output should be
// no inline styles applied
<button class="sc-crzoAe dV0xyD sc-cTJkRt jDcPXG" />
// corresponding style sheet class
.sc-crzoAe {
color: #123456;
}
Keep in mind that React handles some CSS properties like width, height as a shortcut to style={{ width: "100%", height: "50%" }}. That's where the behaviour comes from I think.
One idea I had was to just rename the prop, but it would be nice to have a prop called color to take care of the color.
The correct way to set color is this:
export const CustomButton = styled(Button)`
color: => ${(props) => colors[props.color]};
`
An example can be shown here
The docs for styled-components show its default styled export accepts a single argument:
styled
This is the default export. This is a low-level factory we use to create the styled.tagname helper methods.
Arguments
component / tagname
Description
Either a valid react component or a tagname like 'div'.
I've emphasized "valid react component" here because they explicitly aren't saying this has to be a React component created by styled, although traditionally that is how this is used (as well as documented under their Extending Styled section). An example of this is shown below:
const RedBox = styled.div`
border: 1px solid black;
color: red;
`;
// Traditionally, the argument you pass to `styled` is
// a react element *created by a previous `styled` call*
const BlueBox = styled(RedBox)`
color: blue;
`;
function Example() {
return (
<div>
<RedBox>I am a red box</RedBox>
<BlueBox>I am a blue box</BlueBox>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://unpkg.com/react#17.0.1/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17.0.1/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/react-is#17.0.1/umd/react-is.production.min.js"></script>
<script src="https://unpkg.com/styled-components#5.2.1/dist/styled-components.js"></script>
<div id="root"></div>
Nothing unsurprising with the above.
However, my question is what if you pass a non styled component as the argument to the styled call? Shouldn't that returned element also get the styles applied?
Consider the following simple example:
// Create two simple components, one functional, one class-based
const BoxFunctional = (props) => <div>{props.children}</div>;
class BoxClass extends React.Component {
render() {
return <div>{this.props.children}</div>
}
}
// Here, I pass a functional React Component to `styled`
const RedBoxFunctional = styled(BoxFunctional)`
color: red;
border: 1px solid black;
`;
// Again, passing another regular React component, this time a class component
const RedBoxClass = styled(BoxClass)`
color: red;
border: 1px solid black;
`;
function Example() {
return (
<div>
<p>The below two Boxes are regular react elements:</p>
<BoxFunctional>I am a functional box</BoxFunctional>
<BoxClass>I am a class box</BoxClass>
<hr />
<p>The below two boxes <em>should</em> be styled:</p>
<RedBoxFunctional>I am a functional red box</RedBoxFunctional>
<RedBoxClass>I am a class red box</RedBoxClass>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://unpkg.com/react#17.0.1/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17.0.1/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/react-is#17.0.1/umd/react-is.production.min.js"></script>
<script src="https://unpkg.com/styled-components#5.2.1/dist/styled-components.js"></script>
<div id="root"></div>
Running the above snippet you can see that the styled components are not styled, despite them extending a "valid" react component.
Is there something I'm missing? Are the docs just incorrect? Can styled only apply styles to an existing react component that is made by a previous styled call?
I don't really know where the confusion lies, you can pass any React component to the styled Higher Order Component.
The issue you have is that you aren't trying to style the BoxFunctional or BoxClass components, but rather you are trying to style the JSX they render. You just need to proxy the className prop through to what each renders.
Styling any Component
The styled method works perfectly on all of your own or any
third-party component, as long as they attach the passed className
prop to a DOM element.
const BoxFunctional = (props) => (
<div className={props.className}>{props.children}</div>
);
class BoxClass extends Component {
render() {
return <div className={this.props.className}>{this.props.children}</div>;
}
}
Demo
I have a parent ButtonGroupcomponent that takes in children like this.props.children. The children I'm passing to it is the btnItem component that renders out single buttons. We can add as many of these buttons as we want.
//ButtonGroup Component
render() {
return (
<div>
{this.props.children}
</div>
)
}
//buttonItem component:
render() {
return (
<button disabled={this.props.disabled}>{this.props.caption}</button>
)
}
//final render
<ButtonGroupComponent>
<buttonItem caption="Nothing"/>
<buttonItem caption="Something" disabled={true}/>
<buttonItem caption="Refresh"/>
</ButtonGroupComponent>
^ This is what I get out of the above code.
What I want to achieve is a way for me style the border radius of the first and last item so that they have a curved border. This would have to be dynamic as this styling will be dependent on how many children buttonItem we render.
I should also mention that I'm using styled-components for the css of each button.
you can use first and last child css pseudo selector here. in your Component write following code.
const ButtonGroupComponent= styled.div`
button:first-child {
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
button:last-child {
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
`;
and render function like this
<ButtonGroupComponent>
<buttonItem caption="Nothing"/>
<buttonItem caption="Something" disabled={true}/>
<buttonItem caption="Refresh"/>
</ButtonGroupComponent>
check out this demo
I wrap a quick example here, I didn't test it yet but you could try:
const StyledButtonGroupComponent = styled(
({ willBeStyled, children, ...rest }) =>(
<ButtonGroupComponent {...rest}>{children}</ButtonGroupComponent>
))`
${props => React.Children.toArray(props.children).length <= props.willBeStyled && `
...first child, last child styles go here...
`}
`