React with FlowRouter: How to show/hide component based on route - reactjs

I have a Meteor application that I'm developing using React and I do my routing using FlowRouter. My main AppContainer for the project has a bunch of components, one of them being the footer.
class AppContainer extends Component {
render() {
return(
<div className="hold-transition skin-green sidebar-mini">
<div className="wrapper">
<Header user={this.props.user} />
<MainSideBar />
{this.props.content}
<Footer />
<ControlSideBar />
<div className="control-sidebar-bg"></div>
</div>
</div>
)
}
}
I have a few routes that go to various chat rooms:
Eg.
/chatroom/1
/chatroom/2
/chatroom/3
Is there a way for me to hide the <Footer /> component if the route is a /chatroom/<anything>?

You can maybe do a conditional rendering by checking the current path.
If the <anything> part (I assume it is a parameter) after the /chatroom/ is not important, and if you do not have any other routing that starts with chatroom, you can try this one:
const currentPath = window.location.pathname
{!currentPath.includes('chatroom') ? <Footer /> : null }
So your code would look like this:
class AppContainer extends Component {
render() {
currentPath = window.location.pathname
return(
<div className="hold-transition skin-green sidebar-mini">
<div className="wrapper">
<Header user={this.props.user} />
<MainSideBar />
{this.props.content}
{!currentPath.includes('chatroom')
? <Footer />
: null }
<ControlSideBar />
<div className="control-sidebar-bg"></div>
</div>
</div>
)
}
}
If <anything> part is important and/or you have other routes that starts with chatroom, you can first get the parameter of the route with
const param = FlowRouter.getParam('someParam');
and then do the conditional rendering by checking if the current path contains the chatroom/:param like this:
const currentPath = window.location.pathname
{!currentPath.includes(`chatroom/${param}`) ? <Footer /> : null }
So your code would look like this
class AppContainer extends Component {
render() {
const currentPath = window.location.pathname
const param = FlowRouter.getParam('someParam');
return(
<div className="hold-transition skin-green sidebar-mini">
<div className="wrapper">
<Header user={this.props.user} />
<MainSideBar />
{this.props.content}
{!currenPath.includes(`chatroom/${param}`)
? <Footer />
: null }
<ControlSideBar />
<div className="control-sidebar-bg"></div>
</div>
</div>
)
}
}

You can also use the following syntax below. It accomplishes the same thing as the first example provided by Cagri Yardimci.
{ !currentPath.includes('chatroom') && <Footer /> }

Related

How to send multiple elements as React children and position individually where you want

The structure should look like this:
<Modal>
<Header />
<Body />
<Footer />
</Modal>
The output should look like this
<div>
<header>
<Header />
</header>
<main>
<Body />
</main>
<footer>
<Footer />
</footer>
</div>
You can select the passed children by their index and position them.
Here's how to achieve the solution,
const Modal = ({children}) => {
return (
<div>
<header>
{ children[0] }
</header>
<main>
{ children[1] }
</main>
<footer>
{ children[2] }
</footer>
</div>
)
}
We have this option to specify different spaces for multiple components. In React, we don't but this action can be done by passing props. Like this:
<MyComponent
headerComponent={<Header />}
bodyComponent={<Body />}
footerComponent={<Footer />}
>
</div>
And in MyComponent:
const MyComponent = (props) => {
return (
<div>
{props.headerComponent}
{props.bodyComponent}
{props.footerComponent}
</div>
)
}
const Modal = ({ children }) => {
return (
<React.Fragment>
<header>{children[0]}</header>
<main>{children[1]}</main>
<footer>{children[2]}</footer>
</React.Fragment>
)};
or
const Modal = ({ children }) => {
return (
<>
<header>{children[0]}</header>
<main>{children[1]}</main>
<footer>{children[2]}</footer>
</>
)};
which does the same things.

Cannot Read image of undefined in React app

I'm use the filter method to pick-out one featured item in an array and use that to render it's state in my Home Component. I'm using the filter method in the MainComponent, passing that to the Home Component, where I want the AddCard functional component to render. I'm getting the error message of "cannot read image of undefined" so I'm assuming I'm doing something wrong in passing my props.
MainComponent.js
class Main extends Component{
constructor(props){
super(props);
this.state={
whyfastpack: WHYFASTPACK
};
}
render(){
const LandingPage =()=>{
return (
<Landing />
);
};
const HomePage=()=>{
return (
<Home
lightweight={this.state.whyfastpack.filter(lightweight => lightweight.featured)[0]}
/>
);
};
return(
<div>
<Header />
<Switch>
<Route exact path='/' component={LandingPage} />
<Route path ='/home' component={HomePage} />
HomeComponent.js
...
</div>
<div className='col-md m-1'>
<AddCard />
</div>
</div>
....
function RenderCard({ item }) {
return (
<Card>
<CardImg src={this.image} alt={item.name} />
<CardBody>
<CardTitle>{item.name}</CardTitle>
<CardText>{item.comment}</CardText>
</CardBody>
</Card>
);
}
function AddCard(props) {
return (
<div className="container">
<div className="row">
<div className="col-md m-1">
<RenderCard item={props.lightweight} />
</div>
</div>
</div>
);
}
whyFastPack.js
export const WHYFASTPACK = [
{
id: 0,
image: 'images/pnw.jpg',
name: 'Reduce Weight',
comment: 'Carry only what you need for the day, not a month! With fast-packing you only need to bring enough to get you through each day!',
featured: true,
}
]
You neglected to pass a lightweight prop to AddCard from HomeComponent.
<div className='col-md m-1'>
<AddCard lightweight={this.props.lightweight} />
</div>

Passing props through Link to a child component to complete the url to make an api call

I have a father component (which is a child of HousesGallery and receiving props to display api data), but now I want to display HouseDetail component as a place to show details about the house where you clicked. The api needs the name of the house so I'm trying to pass the name through props via Link and I don't know if I'm missing something in the Route or somewhere else.
App component where the Route is:
export default function App() {
return (
<div className="got-font">
<Router>
<div>
<Menu/>
</div>
<Switch>
<Route path="/detallecasa/:name">
<HouseDetail/>
</Route>
<Route path="/personajes">
<CharactersGallery/>
</Route>
<Route path="/casas">
<HousesGallery/>
</Route>
<Route path="/cronologia">
<Chronology/>
</Route>
<Route path="/">
<HomePage/>
</Route>
</Switch>
</Router>
</div>
);
}
Father component:
export default function HouseComponent(props) {
return (
<div className="container">
<div className="row">
{props.info.map((item, i) =>
item.logoURL ?
<Link to={{pathname: `/detallecasa/${item.name}`, query: {housename: item.name}}}
key={i} className="col-lg-2 col-md-4 col-sm-12 c-houses_div">
<figure className="c-houses_div_figure" key={i}>
<img className="c-houses_div_figure_img" src={item.logoURL} alt=""/>
<figcaption>
<h3>{item.name}</h3>
</figcaption>
</figure>
</Link> : null
)}
</div>
</div>
);
}
And the child component:
export default function HouseDetail(props) {
const [houseDetail, setHouseDetail] = useState([]);
useEffect(() => {
axios.get(process.env.REACT_APP_BACK_URL + "houses/" + props.match.params.housename)
.then(res => {
console.log(res.data)
setHouseDetail(res.data);
})
}, [])
return (
<div className="">
<div className="container">
<div className="row">
{houseDetail.map((item, i) =>
<div key={i} className="">
<figure className="" key={i}>
<img className="" src={item.logoURL} alt=""/>
<figcaption>
<h3>{item.name}</h3>
</figcaption>
</figure>
</div>
)}
</div>
</div>
</div>
);
}
The Link's to prop object doesn't take a query property, but you can pass additional data in route state
<Link
to={{
pathname: `/detallecasa/${item.name}`,
state: { housename: item.name }, // <-- Pass route state, if you wanted to
}}
... // other props, etc..
>
...
</Link>
This issue is more about how you are trying to reference the route's match params in the rendered component.
The access the route match param based on what it is named in the Route's path, i.e. name.
<Route path="/detallecasa/:name"> // <-- match param is `name`
<HouseDetail/>
</Route>
Access correctly, i.e. props.match.params.name.
useEffect(() => {
axios.get(process.env.REACT_APP_BACK_URL + "houses/" + props.match.params.name)
.then(res => {
console.log(res.data)
setHouseDetail(res.data);
})
}, []);

props in app does not work in other component

Currently I'm working on a project for a customer. I am building my frontend in ReactJs and I should use a loggedIn check. While I'm loggedIn I want to change my button Log Out. And by a product I want to show in cart, but it does not show anything at all by loggedIn: true in App.js
Code:
App.js
state = {
name: "Tapijtboerderij",
winkelmand: 0,
gebruiker: null,
product: null,
loggedIn: true
}
render() {
return (
<div className="App" >
<Menu props={this.state} />
}
Menu.js (made in React.Router)
A line of code to router to component.
import React from 'react'
import { BrowserRouter as Router, Route } from "react-router-dom";
import LinkTo from './link';
import Index from "../pages/index";
import Tapijt from "../pages/tapijt";
import Vloeren from "../pages/vloeren";
import Zonwering from "../pages/zonwering";
import Gordijnen from "../pages/gordijnen";
import Contact from "../pages/contact";
import Login from "../pages/login";
import Product from "../pages/product";
import Page404 from "../pages/404";
import AlgemeneVoorwaarden from "../voorwaarden/algemene-voorwaarden";
import CookieBeleid from "../voorwaarden/cookie-beleid";
import PrivacyBeleid from "../voorwaarden/privacy-beleid";
import Sitemap from "../voorwaarden/sitemap";
import logo from "../logo/logo.jpg";
import Button from "./button";
export default class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {
collapsed: false,
widthOverlay: 0
}
}
collapseMenu = () => {
let strepen = document.querySelectorAll(".streep");
if (this.state.collapsed === false) {
strepen[0].style.transform = "rotate(-45deg)";
strepen[0].style.marginTop = "7px";
strepen[1].style.display = "none";
strepen[2].style.transform = "rotate(45deg)";
strepen[2].style.marginTop = "-7px";
this.setState({
collapsed: !this.state.collapsed,
widthOverlay: "60%"
});
} else {
strepen[0].style.transform = "rotate(0deg)";
strepen[0].style.marginTop = "0";
strepen[1].style.display = "block";
strepen[2].style.transform = "rotate(0deg)";
strepen[2].style.marginTop = "0";
this.setState({
collapsed: false,
widthOverlay: 0
})
}
}
render() {
return (
<Router>
<nav className="menu">
<div className="container">
<a href="/" className="brand-logo to-left">
<img src={logo} className={"menu-logo"} alt={"logo bedrijf"} />
</a>
<div className={"links to-right menu-laptop"}>
<div className={"menu-links-laptop"}>
<LinkTo to="/" name={"Home"} />
<LinkTo to="/src/pages/tapijt" name={"Tapijt"} />
<LinkTo to="/src/pages/vloeren" name={"Vloeren"} />
<LinkTo to="/src/pages/zonwering" name={"Zonwering"} />
<LinkTo to="/src/pages/gordijnen" name={"Gordijnen"} />
<LinkTo to="/src/pages/contact" name={"Contact"} />
{this.props.loggedIn ? <Button link={"/src/pages/login"} buttonName={"Log uit"} /> : <Button link={"/src/pages/login"} buttonName={"Log in"} />}
</div>
</div>
</div>
</nav>
<div className="sidenav" id="mobile-demo" style={{
width: `${this.state.widthOverlay}`
}}>
<div className={"menu-icon"} onClick={this.collapseMenu}>
<div className={"strepen"}>
<div className={"streep"} />
<div className={"streep"} />
<div className={"streep"} />
</div>
</div>
<div className={"links-mobile"}>
<LinkTo to="/" name={"Home"} />
<LinkTo to="/src/pages/tapijt" name={"Tapijt"} />
<LinkTo to="/src/pages/vloeren" name={"Vloeren"} />
<LinkTo to="/src/pages/zonwering" name={"Zonwering"} />
<LinkTo to="/src/pages/gordijnen" name={"Gordijnen"} />
<LinkTo to="/src/pages/contact" name={"Contact"} />
<LinkTo to="/src/pages/login" name={"Log in"} />
</div>
</div>
<Route path="/" exact component={Index} />
<Route path="/src/pages/tapijt" component={<Tapijt loggedIn={this.props.loggedIn} />} />
<Route path="/src/pages/vloeren" component={Vloeren} />
<Route path="/src/pages/zonwering" component={Zonwering} />
<Route path="/src/pages/gordijnen" component={Gordijnen} />
<Route path="/src/pages/contact" component={Contact} />
<Route path="/src/pages/login" component={Login} />
<Route path="/src/pages/product" component={Product} />
<Route path="/src/pages/404" component={Page404} />
<Route path="/src/voorwaarden/algemene-voorwaarden" component={AlgemeneVoorwaarden} />
<Route path="/src/voorwaarden/cookie-beleid" component={CookieBeleid} />
<Route path="/src/voorwaarden/privacy-beleid" component={PrivacyBeleid} />
<Route path="/src/voorwaarden/sitemap" component={Sitemap} />
</Router >
);
}
}
Tapijt.js
render() {
return (
<div className={"container"}>
<h1 className={"header"}>Tapijten</h1>
<div className={"parent"}>
<div className={"flex"}>
{tapijten.map((tapijt, identifier) =>
<Card key={identifier}
photo={TapijtImg}
productHeader={tapijt}
productContent={"Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of \"de Finibus Bonorum et Malorum\" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, \"Lorem ipsum dolor sit amet..\", comes from a line in section 1.10.32."}
link={"./product"}
buttonName={"Ga naar Product"}
loggedIn={this.state.loggedIn} />)}
</div>
</div>
</div>
)
}
Product.js
import React from 'react';
import ProductImage from "../images/tapijt.jpg";
import Button from "../components/button";
let productSpecifications = [["Lengte: ", 1200], ["Breedte: ", 1000], ["Soort: ", "Hoogpolig
saxony"], ["Prijs per vierkante M(2): ", 4.85]];
class Product extends React.Component {
constructor(props) {
super(props);
this.state = {
data: productSpecifications
}
}
render() {
return (
<div className={"container top-40"}>
<div className={"flex half-om-half"}>
<div className={"product-photo"}>
<img src={ProductImage} className={"product-img"} alt={"product foto"} />
</div>
<div className={"product-info"}>
<h1 className={"product-header"}>Product Header can Kania</h1>
<p>
Is it possible to use CSS pseudo-classes to select even and odd instances of list items?
I'd expect the following to produce a list of alternating colors, but instead I get a list of blue items:
</p>
<h2 className={"product-price"}>679,-</h2>
{/*{this.props.loggedIn ? <Button link={"#"} buttonName={"In Winkelmand"} /> : ""}*/}
{this.props.loggedIn ? <Button backgroundColor={"var(--success-color)"} color={"var(--white)"} link={"#"} buttonName={"In Winkelmand"} /> : ""}
</div>
</div>
<div className={"product-specifications"}>
<h2>Details</h2>
<table>
<tbody>
{this.state.data.map((data) => <ul>{data}</ul>)}
</tbody>
</table>
</div>
</div>
)
}
}
export default Product;
Thank you in advance
First of all your Route is wrong.
<Route path="/src/pages/tapijt" component={<Tapijt loggedIn={this.props.loggedIn} />} />
It should be
<Route path="/src/pages/tapijt" render={(props) => (<Tapijt loggedIn={this.props.loggedIn} {...props}/>)}/>
Unless you handle it somewhere else not showing in current code snippet, you pass the loggedIn value to Tapijt component but you don't handle it somehow. you are using a state property loggedIn.
Maybe this
buttonName={"Ga naar Product"}
loggedIn={this.state.loggedIn} />)}
should be this?
buttonName={"Ga naar Product"}
loggedIn={this.props.loggedIn} />)}
just to be on the same page.
You are passing your Props Object called state from App.js to the Menu Component.
So I assume that you check for the this.props.state inside the Menu Component right ?
<Route path="/src/pages/tapijt" component={<Tapijt loggedIn={this.props.loggedIn} />} />
Where does this line of code comes from ?
Right now I'm missing the context, where you pass your Props ( loggedIn: true / false ) to your Product.js
It would be great if you could share the could over CodePen or JSFiddle for e.g.
EDIT:
You can try doing it like this:
// Inside of App.js
const userInfo = {
name: "Tapijtboerderij",
winkelmand: 0,
gebruiker: null,
product: null,
loggedIn: true
}
render() {
return (
<div className="App" >
<Menu {...userInfo} />
</div>
}
// Inside of Menu.js
export default class Menu extends React.Component {
...
constructor(props) {
super(props);
this.state = {
collapsed: false,
widthOverlay: 0
}
}
...
render() {
return(
<Router>
<nav className="menu">
<div className="container">
<a href="/" className="brand-logo to-left">
<img src={logo} className={"menu-logo"} alt={"logo bedrijf"} />
</a>
<div className={"links to-right menu-laptop"}>
<div className={"menu-links-laptop"}>
<LinkTo to="/" name={"Home"} />
<LinkTo to="/src/pages/tapijt" name={"Tapijt"} />
<LinkTo to="/src/pages/vloeren" name={"Vloeren"} />
<LinkTo to="/src/pages/zonwering" name={"Zonwering"} />
<LinkTo to="/src/pages/gordijnen" name={"Gordijnen"} />
<LinkTo to="/src/pages/contact" name={"Contact"} />
{this.props.loggedIn ? <Button link={"/src/pages/login"} buttonName={"Log uit"} /> : <Button link={"/src/pages/login"} buttonName={"Log in"} />}
</div>
</div>
</div>
</nav>
...
</Router>
)
}
...
}
// Inside of Product.js
class Product extends React.Component {
constructor(props) {
super(props);
this.state = {
data: productSpecifications
}
}
...
render() {
return(
...
{this.props.loggedIn ? <Button backgroundColor={"var(--success-color)"} color={"var(--white)"} link={"#"} buttonName={"In Winkelmand"} /> : ""}
...
)
}
}
```
By using the Spread Attributes like so: `...userInfoà it also make it easy to pass unnecessary props to components.
I hope it helps you.

Render React nested components

Is there a way to render the children of a React component under different divs?
<Page>
<Header> ... </Header>
<Content> ... </Content>
<Actions> ... </Actions>
</Page>
<div class="page">
<div class="header-wrapper>
<div class="header"> ... </div>
</div>
<div class="content-wrapper">
<div class="content"> ... </div>
<div class="actions"> ... </div>
</div>
</div>
Here I need the wrappers because the header is laid out differently from the area where content and actions are.
The obvious solution may be to introduce a Body component, but is there a more elegant way to abstract the concrete div tree which is needed to represent a certain layout from the logical components (Header, Content, Actions) which the Page declares.
Another solution which works is to pass the Header, Content, and Actions as properties as suggested in the React documentation:
<Page
header={
<Header> ... </Header>
}
content={
<Content> ... </Content>
}
actions={
<Actions> ... </Actions>
}
/>
Thanks,
George
React lets you access individual Children
<Page>
<Header> ... </Header>
<Content> ... </Content>
<Actions> ... </Actions>
</Page>
Inside <Page /> Component render
render() {
// when multiple elements are passed children prop is an array.
// extract each of the element passed from the array and place them
// wherever needed in the JSX
const [HeaderView, ContentView, ActionsView] = this.props.children;
<div class="page">
<div class="header-wrapper>
{HeaderView}
</div>
<div class="content-wrapper">
{ContentView}
{ActionsView}
</div>
</div>
}
Use React.Children.toArray to convert your props.children (that is an opaque data structure) to an array and safely reorganize them.
After convert to array, you can use find or forEach methods to get each one - Header, Content and Action - by testing the type of the item.
const children = React.Children.toArray(this.props.children);
const headerView = children.find(child => child.type.name === 'HeaderView');
const contentView = children.find(child => child.type.name === 'ContentView');
const actionsView = children.find(child => child.type.name === 'ActionsView');
<div class="page">
<div class="header-wrapper>
{headerView}
</div>
<div class="content-wrapper">
{contentView}
{actionsView}
</div>
</div>
Remember to declare HeaderView, ContentView and ActionsView with class or function declaration, not anonymous functions like:
const HeaderView = props => {
// some stateless rendering
};
Otherwise they might have no type.name to test against.
In Aurelia, you can use named slots to achieve this result, but in react the best workaround I have found is to do this:
// ...
const header = <Header someProp={someVal} />;
const content = <Content someOtherProp={someOtherVal} />;
const actions = <Actions action1={someAction} action2={someOtherAction} />;
return (
<Page header={header} content={content} actions={actions} />
);
and in page.jsx:
import React from 'react';
import PropTypes from 'prop-types';
export const Page = ({ header, content, actions }) => (
<div className="page">
<div className="header-wrapper">
<div className="header">
{header}
</div>
</div>
<div className="content-wrapper">
<div className="content">
{content}
</div>
<div className="actions">
{actions}
</div>
</div>
</div>
);
Page.propTypes = {
header: PropTypes.node.isRequired,
content: PropTypes.node.isRequired,
actions: PropTypes.node.isRequired,
};

Resources