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!
Related
I think this question may expand beyond React, but I'm still not sure if React itself is responsible for the problem.
The environment is React with TypeScript. I use CSS imports in the component files, so that each component has its specific stylesheet and I presume that those styles will not be added to the <head> element until the respective component is instantiated. But it turns out that if I import a component from a file, which just reexports all of them, the styles of all the other components, which I do not use, are still added in the DOM.
Here is a simple example, let's say I have two simple components in the lib folder - Avatar and Button. They look like this (the Button is similar):
import React from 'react';
import './avatar.css';
const Avatar: React.FC = (props: any) => {
return (
<div className="avatar">
{props.children}
</div>
);
}
export { Avatar };
Then I add index.ts to reexport the components, in order to have simple import path:
import { Avatar } from './Avatar';
import { Button } from './Button';
export { Avatar, Button };
And finally, in my AppComponent I want to use only the Button component:
import React from 'react';
import { Button } from './lib';
const App: React.FC = () => {
return (
<div className="App">
<Button>example</Button>
</div >
);
}
export default App;
To my surprise, in the <head> element there are <style> tags not only for the Button, but also for the Avatar. Why is this happening? Is my reexport configuration wrong?
Notice that if I import the component directly from its file - import { Button } from './lib/Button' I do not get the Avatar styles.
The example is really simple, but the real scenario is related to a React component library, which contains a lot of components with a lot of stylesheets. I want to avoid inserting so many <style> tags in the DOM, unless they are really needed.
Thank you for spending time on this!
so that each component has its specific stylesheet and I presume that those styles will not be added to the element until the respective component is instantiated
This presumption is wrong. React uses webpack to bundle its files and the way webpack works for CSS imports is that it loads all the CSS files that your project depends on and put them in the <head> element right at the beginning.
You might ask: Then how do I keep my styles separated and don't get them mixed.
There are three solutions to this
A good way is to Add a CSS Modules Stylesheet
Another suggestion is to make the <div> that wraps your component have a className that is the same name as the component so your component will look like this
export default class ComponentOne extends Component {
...
render() {
return(
<div className="ComponentOne">
...
</div
)
}
}
And your component CSS file will look like:
.ComponentOne div img {
...
}
.ComponentOne .class-one {
...
}
With this way, using CSS preprocessor like SASS will come in handy, so your .scss file will simply begin with:
.ComponentOne {
...
}
Another solution is to have the styles as an object inside your component. This way the style will only be scoped to your component and will be removed when the component unmounts, but then you will lose the ability to easily create #media queries andother special effects like:hover` plus this approach is not recommended for small components that get mounted and unmounted too often because this creates a performance issue once the application gets larger
You also might ask: since all the style sheets get imported at the begging, then why don't I put all my styles in one big style sheet and not splitting them up.
Other than the fact that splitting your styles will make them easy to handle so that each component will have its separate CSS file and webpack will handle importing them, There is one other benefit:
Say you have a feature1 component which also has a feature1.css file. In the beginning, when you have feature1 imported in your main app, webpack will also import its style sheet and put it in the <head> element.
But say in the future you decided you don't want to use feature1 component anymore and you are using another feature2 component now which has its own feature2.css file. Now since no other component is importing feature1 component, webpack will also ignore importing feature1.css into the <head> element.
I am trying to create a stateless component in React with the sole purpose of acting as a reusable wrapper. I am also using CSS Modules because I want to have fully modular CSS.
The thing is I don't want to add unnecessary elements (and even more so <div>s), but instead I want to use React's Fragments.
Now, the problem I have is Fragment (at least for now) do not accept classNames. So if I try this:
// In Wrapper.js:
import React, { Fragment } from 'react'
import styles from './Wrapper.css'
const wrapper = (props) => (
<Fragment className={styles.wrapper}>
{props.children}
</Fragment>
)
export default wrapper
In (for example) Navbar.js:
import React from 'react'
import styles from './Navbar.css'
import Wrapper from '../../Layout/Wrapper'
const navBar = (props) => (
<nav className={styles.navBar}>
<Wrapper>
This is the site's main navigation bar.
</Wrapper>
</nav>
)
export default navBar
Now I can of course, use a div instead of the Fragment, but is there any other workaround to avoid using unnecessary markup, of which I am totally unaware at this hour of the night? :)
Thanks in advance for any insight, recommendation, correction, or any other form of help!
Fragments let you group a list of children without adding extra nodes to the DOM. - https://reactjs.org/docs/fragments.html
What Fragments tries to solve it's the unnecessary dom elements but this doesn't mean that Fragments will replace div entirely. If you need to add a className there, it's clear that either you add a dom element in this case another div or add the className to its parent.
Using Fragment means not adding an extra node to DOM.
If you want to assign a className to a node then you'll have to use div.
Create a css file and import it inside your App.js
Create a higher order component withClass.js like below
import React from 'react';
const withClass = (WrappedComponent, className) => {
return props => (
<div className={className}>
<WrappedComponent {...props} />
</div>
);
};
export default withClass;
Import your hoc too.
In your App.js write something like below
<React.Fragment>
<p>Some JSX code here</p>
<React.Fragment>
export default withClass(App, classes.App);
I created .App class inside my css file and imported it so that i can use it later with classes.App. This way you can apply any css class that you create inside your css.You can use the same wrapperComponent to wrap every component you have, by simply importing it and changing export in your component. You just have to make classname of your choice and use it in export statement of your component. When you write props with spread operator(...). All the props from your component will be passed to this wrapperComponent.
PS : English is not my native language so I am not good at explaining but this code will do the trick. Would appreciate a moderator taking look into my explanation.
So the only thing the Wrapper / Fragment does is acting as a CSS wrapper over the children of nav?
I am not very experienced with css-modules, but if I wanted to avoid an extra DOM node just for the className I'd use something like this to get both classNames applied to <nav>:
import React from 'react'
import navStyles from './Navbar.css'
import wrapperStyles from './Wrapper.css'
const navBar = (props) => (
<nav className={`${navStyles.navBar} ${wrapperStyles.wrapper}`}>
This is the site's main navigation bar.
</nav>
)
export default navBar
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.
I'm working with vue component (cli .vue)
I need to have my stylesheet appear only if certain boolean is true/false.
Simplest explanation would be something like :
When myVar==false, component is not loading styles.
<style v-if="myVar" lang="scss"> #import 'mystyles.css' </style>
I know it is impossible in that way, but how I'm able to do 'similar' thing?
I need to load my styles in vue if user want to use default Styles, if not I need to prevent them from being loaded.
My component is used not once but many times in page, but that condition of using/not using default css need to be apply by all components as well, so no problem here.
Any ideas?
Thanks for help or any ideas in advance :)
Using SCSS, you can wrap that CSS in a class, something like this:
<style lang="scss">
.conditional-class {
#import 'stylesheet.scss';
}
</style>
And then use a Vue class binding for that class:
<div :class="{ conditional-class: true }">
...
</div>
This way the CSS won't apply unless the class is active on that element. If you want the styles to apply all over the app, then put that class on the app root element.
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.