I'm new to React.
Quotes from Create React App:
Generally, we recommend that you don’t reuse the same CSS classes across different components. For example, instead of using a .Button CSS class in AcceptButton and RejectButton components, we recommend creating a Button component with its own .Button styles, that both AcceptButton and RejectButton can render (but not inherit).
Following this rule often makes CSS preprocessors less useful, as features like mixins and nesting are replaced by component composition.
I don't think I understand it fully. Why sass nesting and mixing are less useful with composition? Any example?
Does that also mean I shouldn't have global styles, for example, for an input field? If all the input fields in my app look the same or similar, I should create a component for it, just for the styling purpose?
SASS nesting helps ensure your styles don't leak outside the parent class(es). With component composition, this is automatically enforced because you nest components within components.
Mixins are a way of reusing styles across selectors. With component composition, the reusing comes from composing in the JSX instead of CSS.
Old Way
CSS
.button {
color: #fff;
&.button-accept {
background-color: green;
}
&.button-reject {
background-color: red;
}
}
JSX
function AcceptButton() {
return <button className="button button-accept">Accept</button>;
}
function RejectButton() {
return <button className="button button-reject">Reject</button>;
}
React Way
const buttonStyles = { color: '#fff' };
function Button(props) {
return <button style={Object.assign({}, buttonStyles, props.style)}>
{props.children}
</button>;
}
const acceptStyles = { backgroundColor: 'green' };
function AcceptButton(props) {
return <Button style={acceptStyles}>Accept</Button>;
}
const rejectStyles = { backgroundColor: 'red' };
function RejectButton(props) {
return <Button style={rejectStyles}>Reject</Button>;
}
Note that this uses inline-styles, which may not be ideal in real-world situations where you repeatedly render the same element and the styles are present for each element in the DOM. To solve that, check out some of the CSS-in-JS solutions, such as JSS.
Does that also mean I shouldn't have global styles, for example, for an input field? If in my app all the input fields look the same or similar, I should create a component for it, just for the styling purpose?
That depends on whether you are using any UI framework. If you are rolling out your own from scratch, then it might be possible to do so. Otherwise, global styles are almost unavoidable.
Not that this is not a binary decision. Many of the existing CSS frameworks are not written just for React and will not play well with the CSS-in-JS approaches that React encourages.
Related
I am refering to a situation in which i have something like this:
import "./myCss.css"
const BeautyButton = (props) =>{
return (
<a style={{backgroundColor:"red"}} className="blue-background" >hello</a>
);
}
Which property prevails? Is there a general rule for all the properties (fontSize, width, etc)?
There is nothing different between how CSS works in React and with regular HTML. To understand which rules take effect, you'll want to study the concept of Specificity in CSS.
For your specific question, inline styles are considered more specific than styles in external stylesheets, so those styles will prevail.
I want to make one component and use it several times in another component,Since the templates and logic of this component are always the same and the style is different, I decided to create a single component instead of creating multiple components and just dynamically define the style file.
There is a common method called Lazy Load CSS at runtime which I will include a link to but the problem is this method that the css file is added globally and since all css files have classes of the same name the last file in the DOM Added effects all components (that is, capsulation is not used in this method), so this method is ineffective.
https://egghead.io/lessons/angular-lazy-load-css-at-runtime-with-the-angular-cli
Finally I put a picture below that clearly shows what I want to do exactly.
player.component.ts:
#Component({
selector: 'app-player',
templateUrl: './player.component.html',
styleUrls: ['./player-' + number + '.css']
})
export class PlayerComponent implements OnInit {
#Input() number: number;
constructor() {}
ngOnInit(): void {}
}
player.component.html:
<p class="title">player works!</p>
player-1.css:
.title {
color: red;
}
player-2.css:
.title {
color: orange;
}
game.component.html:
<div>
<app-player [number]="1"></app-player>
<app-player [number]="2"></app-player>
<app-player [number]="3"></app-player>
</div>
How can this be done? Any solution is appreciated.
Thanks.
If you are solely looking to dynamically change colors, font sizes etc.. you should reconsider making use of global theming.
Whereas if there are major layout differences you have several options:
Create a base component class
contains all the logic
Derive your other components from this component with different
styling files.
Benifit of the solution above is that you can also use appropriate naming for the
child components which would make the code/directive more readable.
Instead of using numbers 1,2,3 you would have darkListComponent, lightListComponent etc..
Make use of ngClass:
<div [ngClass]="'my-component-style-' + number"></div>
You can still seperate your styling sheets by passing them to your styleUrls in your
component.ts file. (styleUrls: ['theme1.scss','theme2.scss'])
Or you can declare all classes in one file for max styling reusability.
my-component-style-1,my-component-style-2 {
...same styling
},
my-component-style-2 {
color: orange;
}
Option 2 is closer to yours and you'd only have to update your template and styling sheet
for it to work.
I have a 5 different button components, all of which are styled very different from one another and require different props to render. One piece of functionality that I would like all 5 components to have is that after they have been clicked the button loses focus (using .blur()). I have a :focus style for accessibility reasons, but after a button is clicked I need to lose the :focus style.
Rather than me write the exact same functionality across all 5 buttons it makes sense for me to create a separate component to handle this functionality.
I'm new to React so haven't had to do anything like this before so would like to know the best way of tackling the problem as I have read that passing ref via props is a good way of identifying an anti-pattern, but with my current knowledge of React it seems to be the only way of achieving what I want.
If I were to do this inside of one of the Button components which is then rendered on a page it would look like this:
const Page = () => {
const loseFocus = () => {
this.btn.blur();
};
return (
<Button
onClick={loseFocus}
ref={(btn) => { this.btn = btn; }}
>
Toggle
</Button>
);
};
const Button = props => (
<button onClick={props.onClick}>
{props.children}
</button>
);
I don't want to have to do this for every Button component I ever include though, ideally I would have every button automatically lose focus after being clicked.
If I were to do something like this in jQuery I could just target all .btn elements (which is a common class shared by all 5 components) with an event listener like so:
$('.btn').on('click', function() {
$(this).blur();
};
I know I could do something very similar in plain javascript but would this be going against React best practice?
In this case i would like to use little different tactics. I build one button component
with variable properties to fit all my needs aka
<Button type="large" text="cool" link={href}/>
and inside use
render()
{
return <div style={css}>button stuff</div>
}
where css is toggled per click or whatever i want .
there is real power if css per component is used
you can use javascript to control what css will bee for
current instance. even possible to make dynamic css mutations on component state changes etc ...
I am currently using Draft.js in my React app. It is loaded via node_modules and then used in a Component, e.g.:
import { Editor } from 'draft-js'
...
class MyComponent extends React.Component {
render() {
return(
<div>
<h1
style={ styles.header }
>Some header</h1>
<div
style={ styles.editorWrapper }
>
<Editor
style={ styles.editor }
placeholder="Write something!"
/>
</div>
</div>
)
}
}
const styles = {
editorWrapper: {
backgroundColor: '#ececec',
border: '2px solid #888888',
padding: '25px'
},
editor: {
backgroundColor: '#000'
}
}
As described in the React docs, I can style my own Component (and divs etc) using inline styles, like the h1 or the editorWrapper div.
Now trying to style the Editor(node_modules Component) the same way doesn't do anything. I know that with my own Components I could pass the style as props and then use it in the child Component, but I really don't want to go through all the Draft.js code and change their Components.
Is there a clean solution to style 3rd party Components? Or do I have to use good old css. If so, isn't it bad to have some styling in the Components and other styling in external CSS?
draft-js has some hooks you can use to style parts of the Editor component, but generally there is no one way to style 3rd party components. Many third party components will accept props passed in to override styles, material-ui does this quite well. If a third party component doesn't provide an option to pass in custom styles, you can wrap the component in a className and write css to override the default styles. This might not work if the third party component is using inline styles, in which case you either have to use !important all over your css or fork the component entirely. Hope that helps!
I am trying to combine Radium and Material-ui. When I try to apply multiple styles on a single Material-ui component, no style is applied. So, for example, something like this produces no styling applied:
<MenuItem style={[styles.styleOne, styles.styleTwo]} >
Of course, if I do something like:
<MenuItem style={Object.assign({}, styles.styleOne, styles.styleTwo)} >
it works. Is there some way around it or this is the only way to use Radium for combining styles for a Material-ui component? And just to mention, Radium is properly set up, because applying array of styles on, for example, DIV element or works properly.
Also, I am open to any suggestion about styling a React project that uses Material-ui library. Thanks!
For material-ui components in react, we add styles using the className. If i have to add multiple styles in a material component then below are the methods:
Example 1:
<div className={`${style1} ${style2}`}>
Example 2:
import classNames from 'classnames';
<div className={classNames(classes.style1, classes.style2)} />
Specifically for your case (Radium):
What it's doing is merging 2 objects (style1 and style2) into a new anonymous object {} which is what you need to do.
You'll want to be careful when doing this however as you'll need to consider how you merge if both objects define the same key e.g. if style1 and style2 both define a height which do you use?
There's a long list of possible ways to do this on this stackoverflow thread http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically depending on the libraries you're using and your use case they each have their own pros and cons.
Instead of adding classnames, you can also use the clsx module that comes with Material UI and combine your style classes.
{/* using arrays */}
<MyComponent classes={clsx([classes.text, classes.title])} />
{/* using conditions */}
<div className={clsx(classes.root, {
[classes.base]: true,
[classes.closed]: !open,
[classes.open]: open
})]>
{props.children}
</div>
The Material UI Mini Variant Drawer example does a great job showing this module off.
Check out this fiddle: https://jsfiddle.net/Lxh5x2qr/
It uses the JSX spread (...) operator, which is a bit nicer syntax:
styleOne: {
background: 'blue',
color: 'red'
},
styleTwo: {
background: 'green'
},
... style={{...this.styleOne, ...this.styleTwo}} ...
Please notice the the order of object does matter, just like in Object.assign.
We should not forget that MenuItem is not a DOM element, so when we apply style to it, material-ui manipulates it before applying it to the underlying element, and probably this is the reason why using an array does not work.