Concatenating props and static values in style={{}} in React - reactjs

I have a component I am passing a height as props so it can be used at page level like so (minified code)
<Component style={{height}} />
Component.propTypes = {
height: PropTypes.string, //make sure it's a string
}
Component.defaultProps = {
height: "100%", //100% otherwise defined
}
This can be used later as
<Component height="100%"/>
<Component height="50%"/>
<Component height="20%"/>
...
And renders as
<div class="component-blah-blah" styles="height: 100%;"></div>
I want to add overflow-x: hidden to the party but as a default and non-changeable prop. So that regardless of how they use the styles prop, it will always carry out the overflow-x I defaulted. Like so:
<Component height="100%"/>
<div class="component-blah-blah" styles="height: 100%; overflow-x:hidden"></div>
<Component height="50%"/>
<div class="component-blah-blah" styles="height: 50%; overflow-x:hidden"></div>
<Component height="20%"/>
<div class="component-blah-blah" styles="height: 20%; overflow-x:hidden"></div>
I know I can concatenate classes in string and props like this using the --> ` <-- and the $ sign, but not with a double bracket as style requires.
I'm looking for the syntax for something like this
className='${classes.myPropClass} myCSSclass'
which renders as class="myPropClass myCSSclass" , but for inline styles, not classes...and I can't assing overflow to a class. For other complicated reasons

The style prop takes an object. The inner brackets {} are just the syntax for creating an object inline. So just add the desired property to that object in the render function:
const Component = ({height}) => (
<div class="component-blah-blah" styles={{height, overflowX: 'hidden'}}></div>
);

The example code you've given doesn't seem to match the description you wrote along with it. You said (emphasis, mine):
So that regardless of how they use the styles prop, it will always carry out the overflow-x I defaulted.
But the code you've put here doesn't show there being a style prop. It just shows there being a height prop. If the user of this component is only given a height prop to use, there's no way that value could ever overwrite your overflowX style property.
But... if you're trying to allow the consumer to pass in their own style object in the props, and then ensure that they can't overwrite your desire to implement an overflowX feature, you should use a spread operator to effectively concatenate the user's style, with your style, while keeping your style from being overwritten. It would look something like this:
class App extends Component {
render() {
const styleFromProps = { display: 'none' };
return (
<p
style={{
...styleFromProps,
display: 'inherit',
}}
>
Is this displayed???
</p>
);
}
}
render(<App />, document.getElementById('root'));
Here is a live example:
https://stackblitz.com/edit/react-spread-operators-in-styles
Notice in this example that the styleFromProps object has a display value of none. But the contents of the <p> tag still display. Because the hardcoded value for display is listed in the style object after the display value that's passed in. In CSS, if an attribute is declared twice, the last one "wins".
So if you're allowing the user to pass in a style object as a prop, but you want to ensure that they don't overwrite some of the "critical" styles that you're using, you can do it in this way.

Related

Why color appears as HTML attribute on a div?

This code works perfectly, it creates CSS classes with the corresponding color and applies the classes dynamically:
const Div = styled.div`
color: ${({ color }) => color};
`;
export default function Email() {
const [color, changeColor] = useState();
return (
<>
<Div color={color}>Email</Div>
{['red', 'palevioletred', 'blue', 'yellow'].map(x => <button onClick={() => changeColor(x)}>{x}</button>)}
</>
)
}
However, the color attribute appears in the DOM on the div element:
<div class="sc-crHlIS jxntNS" color="yellow">Email</div>
When I change the color prop name to something else like divColor it doesn't show as a DOM attribute. I understand that props appear as HTML attributes when they have the same name as a native HTML attribute, but color should not because it's not a valid HTML attribute.
Why this behavior?
Styled components has a set of HTML attributes that it allows to be passed down to the resulting element. The color attribute is not technically valid for a div element, however it is a HTML attribute, and this is the criteria it follows.
From the docs:
If the styled target is a simple element (e.g. styled.div), styled-components passes through any known HTML attribute to the DOM. If it is a custom React component (e.g. styled(MyComponent)), styled-components passes through all props.
The documentation does not explicitly state that they accept any attribute on any element, however it seems to be the case given this example:
<Div color="red" foo="foo" value="value" name="name" bar="bar">My div</Div>
Results in:
<div color="red" value="value" name="name">My div</div>
Notice how the non HTML attributes are removed, but the others remain.

Styling nested components using styled components

I've been using styled components for very little time.
At the moment I'm trying to do a style override on a nested element and I've having trouble understanding what I'm doing wrong.
So my struture is.
---------------------------Form.js---------------------------
import { FieldWrapper } from './FieldWrapper';
const Form = styled.form`
/** my form styles **/
`;
const StyledFieldWrapper = styled(FieldWrapper)`
/** my FieldWrapper styles **/
input {
/** input overrides **/
padding-bottom: 0.8rem;
height: 2rem;
line-height: 2rem;
box-sizing: content-box;
background-color: pink !important; // added just for visibility
}
`;
const MyForm = props => {
return (
<>
<Form>
<StyledFieldWrapper />
</Form>
</>
);
}
export { MyForm }
---------------------------FieldWrapper.js---------------------------
const Div = styled.div`
/** my div styles **/
`;
const Label = styled.label`
/** my label styles **/
`;
const Input = styled.input`
/** my input styles **/
`;
const FieldWrapper = props => {
return (
<Div>
<Label>
<Input />
</Label>
</Div>
);
}
export { FieldWrapper }
Now what I expect to happen was that the styles in FieldWrapper.js would be overriden by the StyledFieldWrapper element in Form.js, this however does not happen and I have no idea why. I have overrides like this in the past and in this project. Also StyledFieldWrapper does not contain only overrides, it also has style of its own and I can't even see those.
Has anyone had a similar issue?
NOTE: I have tried to use the solution in Styling Nested Components in Styled-Components with no success.
EDIT:
Since you want the styles to apply to a custom component, you also need to manually assign the className generated by styled-components to the top-level element of that component. Something like:
const FieldWrapper = props => {
return (
<Div className={props.className}>
<Label>
<Input />
</Label>
</Div>
);
}
The problem is likely related to CSS Specicifity, meaning that the original css styles defined in FieldWrapper has higher "importance" than the ones in Form. If you inspect your element you can probably see that both styles are applied, but the former has precendence over the latter.
A way to solve that is to either use the !important rule to each of the input styles defined in your Form component. Another would be to add a class to <Input /> and define your styles as myClass.input. Basically anything that would increase the specicifity of the rules you want to apply.
See the above link for more info of how to do that. Also check out Cascade and inheritance:
Once you understand the fact that source order matters, at some point you will run into a situation where you know that a rule comes later in the stylesheet, but an earlier, conflicting, rule is applied. This is because that earlier rule has a higher specificity — it is more specific, and therefore is being chosen by the browser as the one that should style the element.
As we saw earlier in this lesson, a class selector has more weight than an element selector, so the properties defined on the class will override those applied directly to the element.
Something to note here is that although we are thinking about selectors, and the rules that are applied to the thing they select, it isn't the entire rule which is overwritten, only the properties which are the same.
The amount of specificity a selector has is measured using four different values (or components), which can be thought of as thousands, hundreds, tens and ones — four single digits in four columns:
Thousands: Score one in this column if the declaration is inside a style attribute, aka inline styles. Such declarations don't have selectors, so their specificity is always simply 1000.
Hundreds: Score one in this column for each ID selector contained inside the overall selector.
Tens: Score one in this column for each class selector, attribute selector, or pseudo-class contained inside the overall selector.
Ones: Score one in this column for each element selector or pseudo-element contained inside the overall selector.
Here is an example from MDN:
/* specificity: 0101 */
#outer a {
background-color: red;
}
/* specificity: 0201 */
#outer #inner a {
background-color: blue;
}

Check If Prop Exists in Styled Components

I am just getting started with styled components and want to create variations for things like buttons, navbars, etc.
For instance, I'd like to create a dark version of a navbar (wherein the background color would become dark and the text color light).
What I'd like to do is simply add a prop of dark on a component as follows:
<Navbar dark>...</Navbar>
I'd like to do this as opposed to something like this:
<Navbar type="dark">...</Navbar>
However, I'm not sure how to do this. That is, how do I style an element just by checking if the prop name exists (without passing the prop any values)?
Any ideas? Thanks in advance.
styled-components supports passing props to styled components, and those props can be accessed in the CSS tagged template literal (the CSS inside of the backticks) of the styled component receiving those props.
For example, say that the <Navbar /> in your example is a styled <nav> element, then we can define <Navbar /> in a way that takes into account a dark prop:
const Navbar = styled.nav`
background: ${props => props.dark ? 'black' : 'white'}
`
In the example above, we check for the existance of the dark prop. If it was passed, then we give the component a black background color. Otherwise (the default), we give the component a white background color:
<Navbar dark /> // produces component with black background
<Navbar /> // produces the default white background

Change all Components with className="theme" from green to blue on an event in React

In App.css, I have
.theme {
color: green;
}
And I have className="theme" scattered in multiple components.
Is there a way to change the theme color from green to blue on an event?
If not, how should I design my code?
Well, You can create 2 classes named .blue-theme and .green-theme
Whenever, some event occurs,
onClick = (themeCol) => {
this.setState({theme:thmeCol})
}
render(){
return(
<button onClick={()=>onClick('blue-theme')}>Blue theme</button>
<button onClick={()=>onClick('green-theme')}>Green theme</button>
<div className={this.state.theme}> Sample </div>
)
}
You can pass the value of theme.
you can try
const themeClass = "";
if (event-as-wanted){
themeClass="theme";
}
className={themeClass}
also you can use style insted of className in same file
const theme ={
color: '';
};
style={theme}
and change it with events like
if (event-as-wanted){
theme.color = "green";
}
You can conditionally render the <style/> tag to override style definition for the class in the whole document.
class App extends React.Component {
state = {
red: true,
}
render() {
return (
<div>
<div className="foo">Foo</div>
<button onClick={() => this.setState({ red: !this.state.red })}>
Toggle Red
</button>
{this.state.red && <style>{".foo { color: red }"}</style>}
</div>
);
}
}
Keep in mind that inside JSX tags, curly brackets will be picked up by the interpreter and may break the parser. To avoid that, you should put your CSS inside a string like in the example above.
Adding a <style/> tag to CSS document will override any equally specific CSS rules that came before that. Once the condition is no longer met, the style tag will be removed and the original styling will be restored.
in react.js just set the state of color to whatever and on a click event toggle the color
class App extends React.Component {
constructor(props) {
super(props);
this.state = {color: green};
this.changeColor = this.changeColor.bind(this);
}
changeColor() {
const newcolor = this.state.color == green ? blue : green;
this.setState({ color: newcolor});
}
render() {
return (
<div class="theme" style={{color: this.statecolor}}>
<button onClick={this.changeColor}>
</button>
//put all html withing the parent DOM of element with class theme accordingly or it wont render.
</div>
);
}
Make two class, .green-theme{
color:'green'} and similarly, blue theme.
Mantain a REDUX STATE, CURRENT_THEME. Upon event fire, change the redux state accordingly and everywhere, where you want to use CURRENT_THEME, use it using mapStateToProps.
I would rather try to use almost pure CSS solution:
in App.css
#App.first-theme .theme { color:green; }
#App.second-theme .theme { color:blue; }
in App's render:
<div id="App" className={this.state.currentTheme}>
<AnotherComponent1 />
<AnotherComponent2 />
</div>
All you need to do is to change this.state.currentTheme appropriately. You can even use prop injected from the redux.
Almost all other solutions posted here have the same flaw: you have to adapt all your components to use the theme. Using this solution, you are able to change app's appearance without additional code in your components.
Trust me, injecting the same property from redux store/react context for every component will give you headaches and a lot of unnecessary code.
You should also try to avoid generating additional <style> tags - you will end up having plenty of !important and HTML, logic, and CSS in one file. What a mess! Imagine, what would happen if you would like to use SCSS in the future...

Preserve class name when wrapping element with styled-components

I'm leveraging the Bootstrap grid so I'm creating components that render elements that include a class:
const Row = props => (
<div className="row" {...props}>
{props.children}
</div>
);
Now, at times I want my row to be padded, so I attempted to wrap my Row component:
const PaddedRow = styled(Row)`
padding: 20px;
`;
This applies the padding, but when the div is rendered, it is without the class.
Chrome's React plugin shows the following:
<Styled(Row)>
<Row className=".sc-cHds hQgwd">
<div className=".sc-cHds hQgwd"> // No "row" class
Is it not possible to preserve the class name for the wrapped component?
What you're doing with {...props} is spreading the passed-in props. styled-components passes the generated className via these props, and because you apply the spread after your className property it overrides the existing class.
If you were to write out manually what your code does in the end this is what it'd look like:
<div className="row" className="sc-cHds">
This makes it apparent what the issue is: the second className declaration is overriding the first one! The solution if I were to stick strictly to your code would be to concatenate the passed in class manually:
<div {...props} className={`row ${props.className}`}>
This works, however it's not idiomatic usage of styled-components. Rather than wrapping the component with styled(Row) we recommend creating a styled component for that <div />:
const RowWrapper = styled.div`
padding: 20px;
`
const Row = props => (
<RowWrapper className="row">
{props.children}
</RowWrapper>
);
This is the way styled-components is intended to be used, it will also automatically concatenate your Bootstrap class with the automatically generated one.

Resources