Styling based on state instead of props - styled-components

I want to style a component based on it's state (instead of props), which changes with clicking.
However, I don't see any way to do this without pulling state up into a wrapper and passing the down the state to props. Which seems needless. Is there a way to access state with styled-components?
I would like to do something like this:
const styledDiv = styled.div`
background-color: ${(state) => state.x};
`

The solution recommended by the developers of the library is to pull the state up. I have to agree.

Best thing to do is pass in your state into the styled-component as a prop. Like so...
JS
return (
<div>
<StyledDiv customPropName={this.state.x}>example text</StyledDiv>
</div>
);
Styled-Component
const StyledDiv = styled.div`
background-color: ${(props) => props.x};
`

Related

How to change styles of react component using styled components

I have this React component which is a simple button component:
const Button = ({ children }) => <button>{children}</button>;
I tried to pass the above component inside a styled in order to try to change its styles like this:
const StyledButton = styled(Button)`
color: yellow; //does not work
button {
color: yellowgreen; //does not work
}
`;
I am new to styled components so I am not even sure this is possible to do.
Thank you in advance for the help!
When using styled-components for your custom React components, styled-components needs to know where to inject the CSS you want to give to your <button> tag. This is done by passing the className prop to your Button component and passing it as a prop to the <button> tag.
Please try to edit your code like so:
const Button = ({ children, className }) => <button className={className}>{children}</button>;
You can read more about it here Styled Components - Existing CSS.

Extending styled components

I have a Field Formik's component, in order to apply custom CSS I do:
const Input = styled(Field)`
// CSS goes here
`
And use Input component, works fine. However I use exactly the same CSS in many places, so I've extracted those CSS to standalone styled-component called SuperInput
Now, how can extend style-componet? I need something like
const Input = styled(Field)`
// inlucde CSS from SuperInput component here
`
Example code.
import styled from 'styled-components'
const SuperInput = styled.input`
// CSS here
`
import { Field } from 'formik'
import { SuperInput } from 'styled_components/super_input'
const SomeFormComponent = () => (
<>
// How to use here <Field /> that has <SuperInput /> CSS applied?
</>
)
Basically you just need to spread or append inside the template literals to get it to work. you can keep the common CSS something like
import styled, { css } from "styled-components"
const commonCss = css`
background: red;
`
And can use it in your component like this:
const Input = styled(Field)`
// CSS goes here
${commonCss}
color: hotpink;
`;
const Input1 = styled(Field)`
${commonCss}
color: lightblue;
`;
This should allow you to use the common CSS in various styled components.
For more info, you can read through css styled component API
Edit:
Styled components create HOCs.
After the added superInput definition, I understand what you are trying to do. So your superInput is creating a button with the certain css properties which you are trying to reuse. In that case when you are using Field and trying to extend SuperInput which is a button doesnot make sense. Your Field component is by default a input element(text box), it can be checkbox, radio, file input also.Whatever CSS is written in superInput can be extracted the way I mentioned above and used at multiple places. The way you are trying to do is not the way styled component is designed. That's my understanding
Note : I may be wrong here about whether it is possible or not. But that's what i can say according to my awareness . Anyone Feel free to correct me if I am wrong.

How to change static css file on API response in ReactJS

In my project i want to change the background-color and font of text. Both the properties are written in css file.
Project structure is:
|-myProject
|--public
|--src
|--package.json
All my css is written in public directory, and i have an api which give response of background-color and font. Now i want to change the properties background-color and font in css files according to api response.
Instead of trying to modify the base stylesheets, why not set these particular properties using the elements’ style attributes:
const divStyle = {
backgroundColor: /* Some color */,
fontFamily: /* Some font stack */,
};
function HelloWorldComponent() {
return <div style={ divStyle }>Hello World!</div>;
}
(adapted from the React docs)
I think the best way to do this would be to use inline style on the elements you want to change.
On api response -> set
const yourVar={
backgroundColor:##,
fontFamily:##
};
I believe that the answer from MTCoster is the best approach. depending on the structure of your app you could use the new Context API to make some sort of theme provider, so that you could pass custom styles that could be stored on your application state and that is loaded from your backend API. there are some tools that could help you integrate this feature more easily, like Styled-Components.
with Styled components you culd write something like:
import styled from 'styled-components'
import { YourComponentJSX } from '../somewhere'
// Wrap the component where you need your custom styles
const YourStyledComponent = styled(YourComponentJSX)`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
/* Color the border and text with theme.main */
// using the short-if to add a default color in case it is not connected to the ThemeProvider
color: ${props => props.theme.main ? props.theme.main : "palevioletred"};
border: 2px solid ${props => props.theme.main ? props.theme.main : "palevioletred"};
`;
// Define what props.theme will look like
const theme = {
main: "mediumseagreen"
};
render(
<div>
<ThemeProvider theme={theme}>
<App>
<YourStyledComponent>Themed</YourStyledComponent>
</App>
</ThemeProvider>
</div>
);
This way you could wrap your whole app and use custom styles saved on the app state that have been loaded from the backend and use them on really deeply nested ui components
*The code is a modification from the styled-component docs

Why is theme undefined in styled component props?

Here I access theme by passing a callback function to the styled tag. I guess styled calls this callback function with the props as first argument. This works well.
export default function SectionHeading(props: SectionHeadingProps) {
const Heading = styled.h2`
${props => props.theme.green && `
color: green;
`}
`;
return (
<Heading>{propss.children}</Heading>
);
}
In this example I pass an expression that contains the props the component has received. Here, theme is undefined.
export default function SectionHeading(props: SectionHeadingProps) {
const Heading = styled.h2`
${props.theme.green && `
color: green;
`}
`;
return (
<Heading>{props.children}</Heading>
);
}
Why is theme undefined in the second example?
The reason is that these are different "props" and they are evaluated in different times, in the first example, the props are the props passed to the styled component, augmented with theme (provided you used <ThemeProvider .../>. In the second example, it is the props passed to your component.
The injection of the theme is done by styled-component library and only to styled components. Your component doesn't get it (because it is not a styled component).
Btw, your code has redundant nesting and creates a styled component each time it is invoked.
The way to do it is to simply define:
const SectionHeading = styled.div`
${props => (props.theme && props.theme.green && {color: 'green'})};
`;
and then:
export default SectionHeading;
Note that your sample code has a typo in the first part, you wrote {propss.children} (an extra 's').

"Unknown props" warning on console when attaching additional props

I'm using Styled Components v2.1.1.
The documentation says that to avoid unnecessary wrappers we can use the .attrs constructor. See: https://www.styled-components.com/docs/basics#attaching-additional-props
I've tried to use it but I always receive the following warning in the console:
Warning: Unknown props `margin`, `padding` on <input> tag. Remove these props from the element. For details, see (removed )
in input (created by styled.input)
in styled.input (created by InputContainer)
in div (created by InputContainer)
in InputContainer
I've created a Webpack Bin to show the error: https://www.webpackbin.com/bins/-KpKbVG-Ed0jysHeFVVY
Am I using it in the wrong way or is there an issue?
As it seems, the example provided on the styled components website is not an actual usable example.
Since the attrs function creates attributes for the properties you set, the error is correct in saying that the html <input> tag does not support the attributes margin and padding: MDN - HTML input attributes
The error you're seeing was marked as an issue on the styled-components GitHub.
The actual solution is not to literally use the example given in the styled-components documentation. For the full explanation, please refer to the issue report.
The code example given by one of the contributers on Github is to modify the example as follows:
const getSize = p => p.size || '1em'
const Input = styled.input.attrs({
// we can define static props
type: 'password'
})`
color: palevioletred;
font-size: 1em;
border: 2px solid palevioletred;
border-radius: 3px;
/* here we use the dynamically computed props */
margin: ${getSize};
padding: ${getSize};
`;

Resources