Conditionally Render CSS Based On Route (React) - css

Here is my App.js component so far:
function App() {
return (
<div className="App">
<Router>
<NavBar />
<Switch>
<Route exact path='/' component={HomepageLayout} />
<Route exact path='/post/:id' component={Post}/>
</Switch>
</Router>
</div>
);
}
I need to render the CSS value of a class based on the route that is being rendered. When the HomepageLayout component is rendered, I need the following:
styles.css
.ui.inverted.vertical.center.aligned.segment{
position: fixed;
left: 0px;
top: 0px;
width: 100%;
z-index: 2;
}
Then, I need to change the values for this target when the Post component is rendered so that the styles are basically removed:
styles.css
.ui.inverted.vertical.center.aligned.segment{
position: none;
left: none;
top: none;
width: none;
z-index: none;
}
Is there a way to change the styles for a particular CSS class tag based on the route? I have seen examples using , but is there a way to do this with / ?

use it like this
<Component header />
// component
const Component = ({header}) => {
const conditionalClass = header ? 'yesHeader' : 'noHeader';
return (
<p className={conditionalClass}>this is </p>
)
}
you can use this as well

Related

How to find out which Router component is being rendered in React?

I have a Navbar component and I am trying to render it with different CSS styles using styled-components with respect to the component route which is being rendered. How can I check if that component is being rendered or not ?
const NavbarContainer = styled.div`
height: 115px;
font-family: 'Domine', serif;
align-content: center;
align-items: center;
position: absolute;
z-index: 4;
width: 100vw;
overflow: hidden;
&:hover {
background-color: white;
color: black !important;
}
`;
lets say this the component and I want to change position: absolute to position: static in Login page but I want that to stay that as position: absolute in the home page. How can I achieve this?
Create a version of the NavbarContainer specific for the login route and override the position rule, and use layout routes to render the appropriate routes with the appropriate layout/navbar.
Example:
const NavbarContainer = styled.div`
height: 115px;
font-family: 'Domine', serif;
align-content: center;
align-items: center;
position: absolute;
z-index: 4;
width: 100vw;
overflow: hidden;
&:hover {
background-color: white;
color: black !important;
}
`;
const StaticNavbarContainer = styled(NavbarContainer)`
position: static;
`;
...
import { Outlet } from 'react-router-dom';
const NavbarLayout = ({ children }) => (
<>
<NavbarContainer>{children}</NavbarContainer>
<Outlet />
</>
);
const StaticNavbarLayout = ({ children }) => (
<>
<StaticNavbarContainer>{children}</StaticNavbarContainer>
<Outlet />
</>
);
...
<Routes>
<Route
element={(
<NavbarLayout>
// ... render links here as children
</NavbarLayout>
)}
>
<Route path="/" element={<Home />} />
// ... routes to render with absolute positioned navbar
</Route>
<Route
element={(
<StaticNavbarLayout>
// ... render links here as children
</StaticNavbarLayout>
)}
>
<Route path="/login" element={<Login />} />
// ... other routes with static positioned navbar
</Route>
// ... other routes without navbars
</Routes>
One idea would be to wrap your component inside another
like
In Login page
const NavWrapper = styled.div`
>div{
position: static
}
`
<NavWrapper>
<NavbarContainer/>
</NavWrapper>
You can achieve this with conditional styled based on login path
<Appbar style={{ position: pathname === '/login' ? 'static' : 'absolute' }}></Appbar>

React jsx style tag not applied to tags returned by functions

I'm trying to apply styling to tags that are generated from a for loop inside a function. The problem is that styling within a tag doesn't apply to these generated tags. Possibly because they're generated after the styling is applied? I'm not sure. Here's an example:
generateTags = (size) => {
let tags = []
for (var i = 0; i < size; i++) {
tags.push(<img className="image-tag" src={this.state.imagePath} alt="image" key={Math.random() * Math.floor(100000)}/>)
}
return tags
}
render() {
return (
<div className="main">
<div className="left-container">
{this.generateTags(10)}
</div>
<style jsx> {`
.main { <-- This is properly applied
position: relative;
width: 100%;
height: 100%;
}
.image-tag { <-- This doesn't work
position: absolute;
width: 50px;
}
`} </style>
</div>
)
}
The width: 50px is not applied to the image, and nothing I place makes any difference. But when I add styling within the tag like this:
<img className="image-tag" style={{width: "50px"}} src={this.state.imagePath} alt="image" key={Math.random() * Math.floor(100000)}/>
Then the style is applied correctly. Does this mean I can't have css within the style tag if the elements are return from a function?
It looks like you are using Styled JSX. One of the principles of Styled JSX is that the CSS is component specific. Since your <img> tags are being created outside of the render() function where your styles are defined, they are not being applied.
In this instance, I would recommend to instead have a GenerateTags React component, instead of a function. That way, you can generate your tags as needed, as well as apply component specific styling, like so:
GenerateTags = (props) => {
const {size} = props
let tags = []
for (var i = 0; i < size; i++) {
tags.push(i)
}
return(
<>
{tags.map((tag, index) => (
<img className="image-tag" src={this.state.imagePath} alt="image" key={Math.random() * Math.floor(100000)}/>
))}
<style jsx>{`
// This will now work as it is in the same scope as the component
.image-tag {
position: absolute;
width: 50px;
}
`}</style>
</>
)
return tags
}
render() {
return (
<div className="main">
<div className="left-container">
<GenerateTags size={10} />
</div>
<style jsx> {`
.main { <-- This is properly applied
position: relative;
width: 100%;
height: 100%;
}
`} </style>
</div>
)
}
Otherwise, if you wanted these styles to be applied outside of the scope of the component, you could use the global option:
<style jsx global>
...
</style>

Material-UI: Remove TimelineItem missingOppositeContent:before element

I'm using Material-UI and building a timeline. My code is as follows:
<Timeline align="right" className={classes.monthlyContainer}>
<TimelineItem >
<TimelineSeparator className={classes.timelineSeparator}>
<TimelineDot className={classes.timelineDot} />
<TimelineConnector className={classes.timelineConnector} />
</TimelineSeparator>
{(data.map(url =>
<TimelineContent className={classes.memsImageContainer}>
<img
className={classes.memsImage}
src={url}
alt="MEMs"
/>
</TimelineContent>
))}
</TimelineItem>
</Timeline>
When I render the webpage, the Material-UI timeline keeps creating a .MuiTimelineItem-missingOppositeContent:before element which is shifting the layout of my timeline to the left.
When I inspect the element, this is what I see:
<li class="MuiTimelineItem-root MuiTimelineItem-alignRightMuiTimelineItem-missingOppositeContent">
<div class="MuiTimelineSeparator-root makeStyles-timelineSeparator-4">
<span class="MuiTimelineDot-root makeStyles-timelineDot-5 MuiTimelineDot-defaultGrey">
</span>
<span class="MuiTimelineConnector-root makeStyles-timelineConnector-6">
</span>
</div>
</li>
When I inspect the styles, this is what I have:
.MuiTimelineItem-missingOppositeContent:before {
flex: 1;
content: "";
padding: 6px 16px;
padding-left: 0px;
padding-right: 0px;
I have recreated it in codesandbox here
How can I remove this element?
The definition of the default styles for the missingOppositeContent element is as follows:
/* Styles applied to the root element if no there isn't TimelineOppositeContent provided. */
missingOppositeContent: {
'&:before': {
content: '""',
flex: 1,
padding: '6px 16px',
},
},
You can override the default styles using the same structure. Overriding this in the theme would look like the following:
const Theme = createMuiTheme({
overrides: {
MuiTimelineItem: {
missingOppositeContent: {
"&:before": {
display: "none"
}
}
}
}
});
You can also do this on a case-by-case basis (in case you have other situations in your code where you want the missing-opposite-content styling) using withStyles:
const TimelineItem = withStyles({
missingOppositeContent: {
"&:before": {
display: "none"
}
}
})(MuiTimelineItem);
You won't believe that you just need to add the <TimelineOppositeContent> component and set display property as 'none'. And it will be solved.

React Router how to setup transition on parent element

I have a simple router configuration:
<Router history={browserHistory}>
<Route lang="en" path="en" component={App}>
<Route path="*" component={PageFactory} />
</Route>
<Redirect from="*" to="/en/*" />
</Router>
and main App component has the following:
<div className="app">
<Home />
<Sidebar {...sidebarProps}>
{this.props.children}
</Sidebar>
</div>
So, the Home component always opens when you open the react application. When I click a link in Home component (e.g /en/pages/about-page), the contents of the page gets loaded to Sidebar component and Sidebar gets a prop isOpen (That's what ...sidebarProps is for). Everything works fine. When I click a link Sidebar opens, and contents show with overlay etc. However, transitions do not animate. Here is my CSS:
.sidebar {
position: fixed;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
z-index: 200;
visibility: hidden;
background: rgba(0, 0, 0, 0.8);
overflow: scroll;
#include transition(visibility 0s linear 0.3s);
&.is-open {
visibility: visible;
#include transition-delay(0s);
.sidebar-content {
#include transform(translateX(0));
}
}
}
The isOpen prop in Sidebar adds class is-open to root element of Sidebar component. Here is Sidebar component for reference:
const Sidebar = props =>
<aside className={`sidebar ${props.isOpen ? ' is-open' : ''}`}>
<SidebarContent>{props.children}</SidebarContent>
</aside>
;
For testing to see whether it is a React problem, I added a button like the following to my App component:
constructor(props) {
super(props);
this.enableSidebar = this.enableSidebar.bind(this);
this.state = { isOpen: false };
}
enableSidebar() {
this.setState({ isOpen: true });
}
render() {
const sidebarProps = { isOpen: this.state.isOpen };
return (
<div className="app">
<button onClick={this.enableSidebar}>Enable Sidebar</button>
<Home />
<Sidebar {...sidebarProps}>{this.props.children></Sidebar>
</div>
);
}
And when I click the button, sidebar opens smoothly. It is React Router that completely reloads the page on every request, making every page be a static page instead of creating a dynamic application.
How can I change this behavior? How can I make React Router to not reload all components on route change? I am thinking that maybe the structure that I have is problematic. If you also think it is, can you suggest me a solution?
I have found the problem. I had to use React Router Link component instead of HTML anchor element. So instead of using:
About
I have to use:
<Link to="/en/pages/about-page">About</Link>

How to make a sticky footer in react?

I've made a sticky footer higher-level component that wraps other components inside itself:
Footer.js
//this is a higher-order component that wraps other components placing them in footer
var style = {
backgroundColor: "#F8F8F8",
borderTop: "1px solid #E7E7E7",
textAlign: "center",
padding: "20px",
position: "fixed",
left: "0",
bottom: "0",
height: "60px",
width: "100%",
};
const Footer = React.createClass({
render: function() {
return (
<div style={style}>
{this.props.children}
</div>
);
}
});
export default Footer;
Usage:
<Footer><Button>test</Button></Footer>
But it is hiding the contents of the page:
This looks like a common problem, so I searched a bit and found this issue, where is FlexBox is recommended for the sticky footer. But at this demo the footer is at the very bottom of the page, while I need the footer to be always displayed on the page and the content being scrolled inside the above area (like in SO chat). In addition to that, there is an advice to change all the other components with custom stylesheet rules. Is it possible to achieve what I need using styling only the footer component so the code will remain modular?
Here's an idea (sandbox example link).
Include a phantom div in your footer component that represents the footer's position that other dom elements will respect (i.e. affecting page flow by not being position: 'fixed';).
var style = {
backgroundColor: "#F8F8F8",
borderTop: "1px solid #E7E7E7",
textAlign: "center",
padding: "20px",
position: "fixed",
left: "0",
bottom: "0",
height: "60px",
width: "100%",
}
var phantom = {
display: 'block',
padding: '20px',
height: '60px',
width: '100%',
}
function Footer({ children }) {
return (
<div>
<div style={phantom} />
<div style={style}>
{ children }
</div>
</div>
)
}
export default Footer
Much easier idea (following the trend), i imported both bootstrap and reactstrap, used the bootstrap fixed bottom class and workaround with that like this.
class AppFooter extends Component{
render() {
return(
<div className="fixed-bottom">
<Navbar color="dark" dark>
<Container>
<NavbarBrand>Footer</NavbarBrand>
</Container>
</Navbar>
</div>
)
}
There is a much simpler way. I am creating a portfolio site with React, and some of my pages are not very long, so in some devices, like kindle fire hd for example, the footer would not stick to the bottom. And of course to set this up in the traditional fashion with would not work, because the would be wrapped in there. And we don't want that. So this is what I did:
In App.js:
import React, { Component } from 'react';
import {Header} from './components/Header';
import {Main} from './components/Main';
import {Footer} from './components/Footer';
class App extends Component {
render() {
return (
<div className="App Site">
<div className="Site-content">
<div className="App-header">
<Header />
</div>
<div className="main">
<Main />
</div>
</div>
<Footer />
</div>
);
}
}
export default App;
And then in _sticky-footer.css (I use POSTCSS):
:root {
--space: 1.5em 0;
--space: 2em 0;
}
.Site {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.Site-content {
flex: 1 0 auto;
padding: var(--space) var(--space) 0;
width: 100%;
}
.Site-content:after {
content: '\00a0';
display: block;
margin-top: var(--space);
height: 0;
visibility: hidden;
}
The original solution for this was created by Philip Walton: https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/
You can fix this by adding margin-bottom: 60px; to the body of your website. With the 60px being the height of your footer.
.footer{
width: 100%;
position: fixed;
bottom: 0;
}
This should do the trick! Cheers! (:
.App will be the main component you load to your Root.
Assume that the footer is the last child of .App in the document flow
.App {
height: 100vh;
display: flex;
flex-direction: column;
}
footer {
margin-top: auto;
}
I found that if you wrap your 'footer' component in a standard html
<footer>
tag, it pretty much sorts out all of the positioning for you
I wanted to share this solution that worked. I cribbed this from https://react.semantic-ui.com/modules/sticky. Scroll to the bottom of this page and inspect the text 'This is the bottom' to see where I stole it. Its a site built on react so it should work for your situation.
Here it is:
{
padding-top: 50vh;
}
Conceptually, this solution is creating negative space like jacoballenwood's phantom div to push the footer down to the bottom and stick it there. Just add it to your css style class for the footer and adjust the value to taste.
Very late answer, but someone can find this useful. You can, instead of phantom style, set Toolbar. I have build some standard layout for the components, where {children} is component from the parent component - App.js. This is example:
import React from "react";
import { Route } from "react-router-dom";
import { makeStyles } from "#material-ui/core/styles";
import AppBar from "#material-ui/core/AppBar";
import CssBaseline from "#material-ui/core/CssBaseline";
import Toolbar from "#material-ui/core/Toolbar";
import Header from "../components/header";
import Footer from "../components/footer";
import SideBar from "../components/sidebar";
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
},
content: {
flexGrow: 5,
padding: theme.spacing(3),
},
}));
const StandardLayout = ({ children }) => {
const classes = useStyles();
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
<Route path="/" component={Header} />
</AppBar>
<SideBar />
<main className={classes.content}>
<Toolbar />
<br />
{children}
<Toolbar/>
</main>
<AppBar className={classes.appBar}>
<Route path="/" component={Footer} />
</AppBar>
</div>
);
};
export default StandardLayout;
Its rule for me
<footer style={{position:"fixed",bottom:"0"}}>
Try this html code:
/public/index.html
<html lang="en" class="h-100">
<body class="h-100">
<div id="root" class="d-flex flex-column h-100"></div>
...
/src/App.js
<main role='main' className='flex-shrink-0'>
You can follow this template:
react-bootstrap-sticky-footer/public/index.html
react-bootstrap-sticky-footer/src/App.js

Resources