Is it possible to use the spread operator with a styled component in React Native?
I have this component:
const StyledHeaderText = styled.Text`
fontFamily: ${props => props.theme.font};
fontSize: ${props => props.theme.fontSizeSubtitle};
color: ${props => (props.lightMode) ? props.theme.fontColor : props.theme.fontPrimaryColor}
`;
But lets say that in my theme, I have an object that has both the fontFamily and the fontSize, and I re use all over the app. I would like to be able to know if I can do something like this, which currently it is not working:
const StyledHeaderText = styled.Text`
...${props => props.theme.fontHeader};
color: ${props => (props.lightMode) ? props.theme.fontColor : props.theme.fontPrimaryColor}
`;
This would be useful too setting up elevation in iOS for example, since I have to setup 4 styles.
Thanks
You can use the css helper function to generate the specific css and return it as a template literal.
import styled, {css} from 'styled-components/native'
const GlobalStyles = css`
fontFamily: ${props => props.theme.font};
fontSize: ${props => props.theme.fontSizeSubtitle};
`
const StyledHeaderText = styled.Text`
${GlobalStyles}
// Other Styles
`
or conditionally as
const StyledHeaderText = styled.Text`
${props => props.theme.fontHeader ? GlobalStyles : 'fontSize: 14'}
// Other Styles
`
In React.js you can use the spread operator override multiple styles
Here is a link to a code example
const StyledText = styled.div`
/* Normal styles */
color: blue,
background-color: white,
/* Fields to override with object from props */
${({ styleOverrides }) => ({ ...styleOverrides })}
`;
You can also return objects directly from interpolations function, and they'll be treated as inline styles.
const StyledHeaderText = styled.Text`
${props => ({...props.theme.fontHeader})};
color: ${props => (props.lightMode) ? props.theme.fontColor props.theme.fontPrimaryColor}
`;
or
const StyledHeaderText = styled.Text`
${props => props.theme.fontHeader};
color: ${props => (props.lightMode) ? props.theme.fontColor props.theme.fontPrimaryColor}
`;
for more details : reference
To expand on previous answers you can also do the following:
import styled, {css} from 'styled-components/native'
// with theme
const GlobalStyles = css`
fontFamily: ${ ({theme}) => theme.font };
fontSize: ${ ({theme}) => theme.fontSizeSubtitle };
`
// Without theme using inherited props
const GlobalStyles = css`
fontFamily: ${ ({font}) => font };
fontSize: ${ ({fontSizeSubtitle}) => fontSizeSubtitle };
`
// if you wanted it to be conditional
const GlobalStyles = css`
fontFamily: ${ ({font}) => font || 'roboto' };
fontSize: ${ ({fontSizeSubtitle}) => fontSizeSubtitle || '14px' };
`
const StyledHeaderText = styled.Text`
${GlobalStyles}
// Other Styles
`
// same can also be done with regular styled components:
export const HeaderText = styled.p`
fontSize: ${ ({fontSizeSubtitle}) => fontSizeSubtitle || '14px' };
`
// Useage would be in your component would be like:
import react from react;
import { HeaderText } from '../component/styles'
export const FooComponent = memo(({ destructuredProps }) => {
// some other code
return (
<>
<HeaderText className={fooClass} fontSize='18px'> Here's my sample text! </HeaderText>
<>
)});
Related
The problem is this:
There is a created UiInput React component using styled-components, above it there is also a UiInputExt React component, which should override some of the styles defined in UiInput, but for some reason this does not happen, apparently it does not even add the corresponding class...
I attach the code below:
const StyledInput = styled.input`
color: ${(props) => props.styles.color};
::placeholder,
::-webkit-input-placeholder {
color: ${(props) => props.styles.color};
}
:-moz-placeholder {
color: ${(props) => props.styles.color};
opacity: 1;
}
::-moz-placeholder {
color: ${(props) => props.styles.color};
opacity: 1;
}
:-ms-input-placeholder {
color: ${(props) => props.styles.color};
}
`;
<StyledInput
id={id}
className={cn(styles.input, classes)}
type={type}
placeholder={placeholder}
styles={isTheme.styles}
>
</StyledInput>
And the corresponding override (which doesn't work!)
import UiInput from '../';
const StyledUiInputExt = styled(UiInput)`
color: ${(props) => props.styles.color_uiinputext};
::placeholder,
::-webkit-input-placeholder {
color: ${(props) => props.styles.color_uiinputext};
}
:-moz-placeholder {
color: ${(props) => props.styles.color_uiinputext};
opacity: 1;
}
::-moz-placeholder {
color: ${(props) => props.styles.color_uiinputext};
opacity: 1;
}
:-ms-input-placeholder {
color: ${(props) => props.styles.color_uiinputext};
}
`;
<StyledUiInputExt classes={cn(styles.input, classes)} {...props} styles={isTheme.styles}></StyledUiInputExt>
You are not exporting the styled component StyledInput, rather you are exporting a plain React component that is rendering StyledInput.
const StyledInput = styled.input`
color: blue;
`;
const UiInput = () => {
return <StyledInput />;
};
export default UiInput;
UiInputExt
import UiInput from "./UiInput";
const StyledInputExt = styled(UiInput)`
color: green;
`;
const UiInputExt = () => {
return <StyledInputExt />;
};
The style you are applying to UiInput isn't applied to the JSX it renders, i.e. it isn't passed through to StyledInput.
You can only override other styled-components. For this you should export/import StyledInput (renamed to UiInput) directly.
const UiInput = styled.input`
color: blue;
`;
export default UiInput;
An alternative would be to export UiInput as a styled component and ensure that the className prop is passed through to the StyledInput component.
const StyledInput = styled.input`
color: blue;
`;
const UiInput = ({ className }) => {
return <StyledInput className={className} />;
};
export default styled(UiInput)``;
I am new to styled components. In my React code I have some conditional rendering to change some CSS depending on if I scroll my navbar with the following code:
const [colorChange, setColorchange] = useState(false)
const changeNavbarColor = () => {
if (window.scrollY >= 80) {
setColorchange(true)
} else {
setColorchange(false)
}
}
window.addEventListener("scroll", changeNavbarColor)
<nav className={colorChange ? "navbar colorChange" : "navbar"}>content</nav>
My problem is to write the styled component for this to work.
My normal CSS looked like this:
.navbar {
/*styles...*/
}
.navbar.colorChange {
/*styles...*/
}
I started by creating the following with some styles:
const Navbar = styled.nav`
/*styles...*/
`
But how do I share the styles that are common for both navbar and colorChange; I tried appending the following to the NavBar styled component:
const Navbar = styled.nav`
/*styles...*/
.colorChange{
/*styles...*/
}
`
And then when rendering the component in React, how do I convert this line to use the styled components instead?
<nav className={colorChange ? "navbar colorChange" : "navbar"}>content</nav>
You need pass colorChange state to NavBar component.
This is NavBar declaration
const NavBar = styled.nav`
//styled for nav bar
color: ${props => props.color}
`
And use it in component
<NavBar color={colorChange ? '#fff':'#ddd'}>{children}</NavBar>
Just pass prop as dynamic value
const Navbar = styled.nav`
// navbar common css
color: ${({ color}) => color};
`
<Navbar color={colorChange ? 'red':'transparent'} />
as you commented you have multiple styles I suggest below approach just pass on prop for isColorChange
const Navbar = styled.nav`
font-size: 13px; //common style
background: ${props => (props.isColorChange ? "#6495ED" : "#2b2b2b")};
background-color: ${props => (props.isColorChange ? "#6495ED" : "#2b2b2b")};
margin-top: ${props => (props.isColorChange ? "10px" : "20px")};
padding-top: ${props => (props.isColorChange ? "5px" : "10px")};
padding-bottom: ${props => (props.isColorChange ? "5px" : "10px")};
`;
<Navbar isColorChange={true / false}></Navbar>;
i have the componet like below, when isOpen state true then should add margin-top 0, margin-bottom: 0, margin-right and margin-left: 4px. if isOpen state false then add margin-right: 4px and margin-bottom: 4px.
below is my code,
function Parent() {
return (
<Wrapper isOpen={isOpen}/>
//Some content
</Wrapper>
);
}
const Wrapper = styled(somediv)<{ isOpen: boolean | undefined;}>`
width: ${props => (props.isOpen ? '32px' : '40px')};
margin: 0 4px; //should modify this based on isOpen true or not.
`;
Could someone help me with this? Thanks.
Using the same styled component approach as you used.
It will work like following:
function Parent() {
return (
<Wrapper isOpen={isOpen}/>
//Some content
</Wrapper>
);
}
const Wrapper = styled(somediv)<{ isOpen: boolean | undefined;}>`
width: ${props => (props.isOpen ? '32px' : '40px')};
margin: ${props => (props.isOpen ? '0 4px' : '0 4px 4px 0')};
`;
Something like this?
<Wrapper isOpen={isOpen} style={{ isOpen===true?
{marginRight:'0px';marginLeft:'4px'} : {marginRight:'4px';marginLeft:'4px';} }}/>
//Some content
</Wrapper>
Probably the easiest way would be to create two object each one with different properties and add them with inline styles.
const styles1 = {};
const styles2 = {};
<Wrapper style={{isOpen ? styles1 : styles2}}/>
//Some content
</Wrapper>
I'm not sure if I understand correctly but using ternary operator it should look like this
const Wrapper = styled(somediv)<{ isOpen: boolean | undefined;}>`
width: ${props => (props.isOpen ? '32px' : '40px')};
margin: ${props => (props.isOpen ? '0 4px' : '0 4px 4px 0')}
`;
If you want to use className instead of inline-styling, I recommend using makeStyles from Material UI as below
// with npm
npm install #material-ui/styles
and then,
import React, {useState} from 'react';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
myClass: {
marginBottom: isOpen => isOpen ? 4 : 0,
marginTop: 4,
width: isOpen => isOpen ? 32 : 40
}
}))
const Parent = () => {
const [isOpen, setOpen] = useState(false)
const classes = useStyles(isOpen)
return <div className={classes.myClass} />
}
Then you can use the same for other attributes and classes.
im trying to create a component whose css outputs a box-shadow with a number of shadows to create a stacked paper effect. Im trying to do this with styled-components. Not having much joy passing the css to the prop. Thanks in advance.
import React from 'react'
import styled from 'styled-components'
const shadows = []
const shadowMixin = (shadow) => {
let i ='';
for(i = 0; i <= shadow; i++){
shadows.push(`${i}px ${i}px ${i % 2 ? "red" : "black" }`)
}
}
shadowMixin(10)
console.log([...shadows].join(','))
const Input = styled.input.attrs(({ size }) => ({
// we can define static props
type: "password",
// or we can define dynamic ones
boxShadow: size || "2px 2px black"
}))`
box-shadow: ${props => props.boxShadow}
`
// const Wrapper = styled.div`
// `
export default() => <Input size={[...shadows].join('')} />;
You are applying your box shadow as an element attribute rather than as a style.
Try this instead:
const Input = styled.input.attrs(() => ({
type: "password"
}))`
box-shadow: ${props => props.boxShadow || "2px 2px black"};
`
...and then pass the boxShadow prop:
export default () => <Input boxShadow={[...shadows].join('')} />
I'm having trouble creating a simple animation in React-Pose. The two problems are
1) I can't get the animation to revert to the initial condition. The hovering variable is changing to false when the mouse leaves, but it the animation doesn't change back.
2) I can't manipulate the animation, I wanted to have a longer duration and maybe an ease out or something, but its just an instant snap to the hovered status.
import React, { useState } from 'react';
import styled from 'styled-components';
import posed from 'react-pose';
import { render } from 'react-dom';
const UpFor = () => {
const [hovering, setHovering] = useState(false);
const HoverContainer = posed.div({
hoverable: true
})
const Container = styled(HoverContainer)`
font-family: 'Baumans';
font-size: 220px;
display: flex;
cursor: pointer;
`
const Up = styled.div`
color: #81D6E3;`
const Four = styled.div`
color: #FF101F
`
const Fours = styled.div`
display: flex;
`
const MirroredFour = posed.div({
unhovered: {transform: 'rotatey(0deg)'},
hovered: {transform: 'rotateY(180deg)',
transition: {
type: 'tween',
duration: '2s'
}}
})
const SecondFour = styled(MirroredFour)`
color: #FF101F
position: absolute;
transform-origin: 67%;
`
return (
<Container onMouseEnter={() => {setHovering({ hovering: true }), console.log(hovering)}}
onMouseLeave={() => {setHovering({ hovering: false }), console.log(hovering)}}>
<Up>Up</Up><Fours><Four>4</Four>
<SecondFour pose={hovering ? "hovered" : "unhovered"}
>4</SecondFour></Fours>
</Container>)
}
export default UpFor
There were two main issues with your code:
duration does not appear to support string values like '2s'. I changed this to 2000.
You were defining your components (e.g. using styled.div, posed.div) inside of your render function. This caused these components to be treated by React as unique component types with each re-render. This results in those components being unmounted and re-mounted each render which prevents transitions from working since the element isn't changing -- instead it is being replaced by a new component of a different type.
Below is a working version of your code which moves the component definitions outside of the render (UpFor) function. You can play around with it in the sandbox provided.
import React, { useState } from "react";
import styled from "styled-components";
import posed from "react-pose";
const Container = styled.div`
font-family: "Baumans";
font-size: 220px;
display: flex;
cursor: pointer;
`;
const Up = styled.div`
color: #81d6e3;
`;
const Four = styled.div`
color: #ff101f;
`;
const Fours = styled.div`
display: flex;
`;
const MirroredFour = posed.div({
unhovered: { transform: "rotateY(0deg)" },
hovered: {
transform: "rotateY(180deg)",
transition: {
type: "tween",
duration: 2000
}
}
});
const SecondFour = styled(MirroredFour)`
color: #FF101F
position: absolute;
transform-origin: 67%;
`;
const UpFor = () => {
const [hovering, setHovering] = useState(false);
console.log("hovering", hovering);
return (
<Container
onMouseEnter={() => {
setHovering(true);
}}
onMouseLeave={() => {
setHovering(false);
}}
>
<Up>Up</Up>
<Fours>
<Four>4</Four>
<SecondFour pose={hovering ? "hovered" : "unhovered"}>4</SecondFour>
</Fours>
</Container>
);
};
export default UpFor;