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
Related
I am having some trouble with css modules in react I dont know how to use react modules in a dynamic way
import classnames from 'classnames'
import styles from './hover.module.css
///
///
const [flashElements,setFlashElements]=useState(elementList.map(element => {
return element.classes.flash
}))```
///
///
I want to be able to display the classes showing the value that corresponds to the element in state . is this even possible or should I approach the problem differently
I want to be to do some thing like the code below
return (
<a classname={styles.HOVER ,styles.flashElements[i]}>
Instead of import styles from './hover.module.css' try using import './hover.module.css' in your component and you can directly use the class names from your CSS file.
Use classNameinstead of classnamein your <a> tag
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'm trying to embed a tockify calender into a component, within a react project I'm building. I should note that I'm using a library called react-script-tag that allows me to use <script/> tags within my component.
Anyway, the calender is rendering - but then keeps on re-rendering as if it's stuck in some sort of loop. I have a feeling I need to implement some sort of lifecycle method. Any suggestions? Code as follows:
import React from 'react'
import Nav from './Nav'
import ScriptTag from 'react-script-tag'
class Events extends React.Component {
render() {
return (
<div>
<Nav/>
<div data-tockify-component="calendar" data-tockify-calendar="hzevents2"></div>
<ScriptTag isHydrating={false} data-cfasync="false" data-tockify-script="embed" src="https://public.tockify.com/browser/embed.js"></ScriptTag>
</div>
)
}
}
export default Events
Use React.PureComponent instead of React.Component because of React.PureComponent will prevent to re-rendering if there is no need or update.
The documentation of 'react-script-tag' says
It is recommended that the Script tag is placed in a component that
only renders once in the entire life of your app. Otherwise, a new
tag will be appended each time the component mounts again.
There are plans down the road to prevent this.
You may want to use a pure component to prevent rerendering.
Edit: Regardless of my answer you should show the rest of your code to be able to detect the problem.
I am writing an application in react and need a way to switch between dark mode and light mode. I could not find any document within blueprintjs docs which mentions how to change theme of all child components with some parent prop configuration.
Use class name .bp3-dark in your root container like below.
Blueprint provides two UI color themes: light and dark. The light theme is active by default. The dark theme can be applied by adding the class bp3-dark to a container element to theme all nested elements.
import React from 'react';
import {Navigation} from "./Navigation";
import Main from "./Main";
const App = () => (
<div className="bp3-dark">
<Navigation />
<Main />
</div>
);
export default App;
I want to implement modals within my React app. Portals seems to be quite nice for this but I don't want to change my outer HTML-structure.
The HTML-should still be:
<div id="app"></div>
I don't want to add an additional div to the HTML-structure.
In the App.js I tried to add the root-modal-container like this:
class App extends Component {
render() {
return (
<ResponsiveProvider>
<div id="root-modal"></div>
<Modal>
<div>Modal :-)</div>
</Modal>
</ResponsiveProvider>
);
}
}
But when trying to getElementById in the Modal-component I always get the error: appendChild on null...
The problem is, that the root-modal-div isn't renderend when initiating the Modal-component.
Any solutions how I can render Modals right in the level after from anywhere in my app?
You can see the not-running-code here. Uncomment the second line in HTML to get the code running.
You can do this with React portals, but it's a little awkward, and you have to get quite involved with the DOM itself to render across an app like this (rather than to totally external DOM nodes).
I've just released a library to fix this exact problem, since although it's possible with portals, it's not easy.
You can see the full details at https://github.com/httptoolkit/react-reverse-portal.
In your example, a solution would look something like:
import React from "react";
import ReactDOM from "react-dom";
import * as portals from "react-reverse-portal";
class App extends React.Component {
constructor(props) {
super(props);
// Create a portal node: the link between the two ends of the portal
this.modalNode = portals.createPortalNode();
}
render() {
// Place an OutPortal somewhere: this is where the content will appear.
// Could be many levels deep in your app - you just need to get the modalNode
// there. For complex cases you might want to put it in a context to distribute it.
return <div>
<Child modalNode={this.modalNode} />
<portals.OutPortal node={this.modalNode} />
</div>;
}
}
const Child = props => {
// Place an InPortal somewhere: this is where the content is defined
return <portals.InPortal node={props.modalNode}>
<Modal>
<div>Modal :-)</div>
</Modal>
</portals.InPortal>
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
In the above: Child defines some modal content it wants to appear, and App defines the space where modal content should appear. You can use this to link parts of your app, and send rendered content between them. Hope that helps!