props in app does not work in other component - reactjs

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.

Related

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>

Parent and child relations in React

I'm having some issues with parsing refs to child classes using ReactJS
Here is my main class where all my refs are located :
class App extends Component {
render() {
return (
<div className="App">
<Navbar />
<Title content="I am Tristan Vermeesch" />
<hr />
<Bio />
<Introduction ref={(Introduction) => { this.introref = Introduction; }} />
<Timeline ref={(Timeline) => { this.timelineref = Timeline }} />
<Projects ref={(Projects) => { this.projectsref = Projects }} />
<Skills ref={(Skills) => { this.skillsref = Skills }} />
<Certificates ref={(Certificates) => { this.certifref = Certificates }} />
<Download ref={(Download) => { this.downloadref = Download }} />
<Contact ref={(Contact) => { this.contactref = Contact }} />
</div>
);
}
}
I want that a Child component in my <Navbar /> retrieves the ref to make it scroll down using react-scroll-to-component. Here is my Navbar class :
class Navbar extends Component {
render() {
return (
<div className="navbar" >
<Route path="/textversion" component={TextVersion} />
<Navitem dName={"Text Version"} url={"textversion"} />
<Navitem dName={"Contact"} url={"contact"} />
<Navitem dName={"Resume"} url={"resume"} />
<Navitem dName={"Certificates"} url={"certificates"} />
<Navitem dName={"Skills"} url={"skills"} onClick={() => scrollToComponent(this.skillsref, { duration: 1600 })} />
<Navitem dName={"Projects"} url={"projects"} />
<Navitem dName={"Life"} url={"life"} />
<Navitem dName={"Me"} url={"me"} />
</div>
)
}
}
You can see the onclick function there, it works in the main class but how can I get all the refs from the parent class?
Try something like this:
You have to define this in your app class
introref =React.createRef()
timelineref = React.createRef()
projectsref = React.createRef()
skillsref = React.createRef()
certifref = React.createRef()
downloadref = React.createRef()
contactref = React.createRef()
and use like this:
<Introduction ref={this.introref} />
<Timeline ref={this.timelineref} />
<Projects ref={this.projectsref} />
<Skills ref={ this.skillsref} />
<Certificates ref={this.certifref} />
<Download ref={this.downloadref } />
<Contact ref={this.contactref} />
and yes you have to crate props in your NavBar:
<Navbar
introref={this.introref}
timelineref={this.timelineref}
projectsref={this.projectsref}
skillsref={this.skillsref}
certifref={this.certifref}
downloadref={this.downloadref }
contactref{this.contactref}
/>
And inside NavBar you have to use like this:
const { introref,timelineref,projectsref,skillsref,certifref,downloadref,contactref } = this.props
and you can us the ref like call
introref.current
timelineref.current
so in your code something like:
<Navitem dName={"Skills"} url={"skills"} onClick={() => scrollToComponent(skillsref.current, { duration: 1600 })} />
I hope it will work

How can I get a component to update on path change while using the same route and component?

When the user is viewing another users profile, and attempt to click their own profile link in the navbar, the component never updates. In both the Redux and React dev tools, it shows that the state has been updated correctly but the component doesnt seem to notice and update.
class App extends Component {
render() {
return (
<Router>
<div>
<Navbar />
<NavbarFix />
<Switch>
<Route exact path="/" component={Posts} />
<Route exact path="/submit" component={AuthRoute(Submit)} />
<Route exact path="/signup" component={AlreadyAuth(SignUp)} />
<Route exact path="/login" component={AlreadyAuth(LogIn)} />
<Route exact path="/user/:id" component={AuthRoute(User)} />
<Route exact path="/item/:id" component={AuthRoute(Item)} />
<Route exact path="/admin" component={AdminAuth(Admin)} />
<Route exact path="/banned" component={Banned} />
<Route component={NoMatch} />
</Switch>
</div>
</Router>
);
}
}
.
class User extends Component {
constructor(props) {
super(props);
this.state = {
posts: [],
user: [],
comments: []
};
}
componentDidMount() {
this.loadUser();
}
loadUser = () => {
API.findUserById(this.props.match.params.id)
.then(res => {
console.log(res.data);
this.setState({
user: res.data.user,
posts: res.data.user.Posts,
comments: res.data.comments
});
console.log(this.state)
})
.catch(err => console.log(err));
this.setState(this.state)
}
handleCommentDelete = id => {
API.deleteComment(id)
.then(res => this.loadUser())
.catch(err => console.log(err));
}
handlePostDelete = id => {
API.deletePost(id)
.then(res => this.loadUser())
.catch(err => console.log(err));
}
render() {
return (
<div>
<div className="container-fluid">
<div className="row">
<div className="col-4 user-data-container">
<div className="row">
<div className="col-12 text-center">
<h2>{this.state.user.username}'s Posts</h2>
</div>
</div>
<hr className="pb-4" />
<div className="row">
<div className="col-12">
{this.state.posts.length > 0 ?
this.state.posts.map(post => (
<PostContainer handledelete={this.handlePostDelete} post={{ ...post, User: this.state.user }} key={post.id} check={this.props.id} />
))
:
<h1>No Posts To Show!</h1>
}
</div>
</div>
</div>
<div className="col-4 user-data-container">
<div className="row">
<div className="col-12 text-center">
<h2>{this.state.user.username}'s Comments</h2>
</div>
</div>
<hr className="pb-4" />
<div className="row">
<div className="col-12">
{this.state.comments.length > 0 ?
this.state.comments.map(comments => (
<CommentContainer verified={this.state.user.verified} handledelete={this.handleCommentDelete} check={this.props.id} comment={comments} className="hover-effect single-comment" key={comments.id}/>
))
:
<h1>No Comments To Show!</h1>
}
</div>
</div>
</div>
<div className="col-4 user-data-container">
<div className="row">
<div className="col-12 text-center">
<h2>{this.state.user.username}'s Information</h2>
</div>
</div>
<hr className="pb-4" />
<div className="row">
<div className="col-12">
<UserContainer user={this.state.user}/>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
username: state.auth.username,
id: state.auth.id,
email: state.auth.email,
profileImg: state.auth.profileImg,
verified: state.auth.verified
};
};
export default withRouter(connect(mapStateToProps)(User));
I believe this has to do with the same route and component being used, so the change isn't actually noticed. Is there any way to fix this? To my understanding, the component should be updating on state change.
If the link is directing to the same route with just a different param, it will not remount/componentDidMount will not be called again.
So, you could use the componentWillReceiveProps(newProps) lifecycle function and look for newProps.match.params.id and compare it with this.props.match.id and if different call loadUser again.
**You would also need to change your loadUser function to accept an id param
(Param validation not included)
componentDidMount() {
this.loadUser(this.props.match.id);
}
loadUser = (id) => {
API.findUserById(id)..
…
}
componentWillReceiveProps(newProps) {
if(newProps.match.params.id !== this.props.match.id) {
this.loadUser(newProps.match.params.id)
}
}

React Router does not update component

I got a component here which should display gifs from giphy on a route base search term:
public render(){
return(
<div>
<Router>
<div>
<Route path='/:term' component={this.dispGiphy} />
</div>
</Router>
</div>
)
}
private readonly dispGiphy = (props) => {
this.getGifs(props.match.params.term)
return(
this.state.items.map(data => (
<img className="images" src={data.images.downsized.url} />
)))
}
public getGifs(inputValue){
// Get Request to the API and write the result to this.state.items
fetch("https://api.giphy.com/v1/gifs/search?limit=3&api_key=KEY_GOES_HERE&q=" + inputValue)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.data
});
},
(error) => {
this.setState({
error,
isLoaded: true
});
}
)
}
I also got a list, listing previous search terms:
public render(){
return(
<div>
<ul id="ul_LeftList">
{this.state.terms.map((term) => (
<li key={term}>
<Link to={`${term}`}>{term} </Link><button className="fa fa-cancel btnDelete" onClick={this.deleteElement(term)}
>X</button>
</li>
))}
</ul>
</div>
)
}
These two components are bound together in the app.tsx
return(
<div>
<div><header className="App-header">
<input type="text" placeholder="Search..." onChange={this.searchChanged} />
<button type="submit" className="fa fa-search searchButton" onClick={this.process} />
</header>
</div>
<Router>
<p className="leftList">
<LeftList terms={this.state.searchterms} />
</p>
</Router>
<p className="App-content">
<Router>
<Route path='/:term' component={Giphy} />
</Router>
<Giphy />
</p>
</div>
)
Now, when I click on a Link in the LeftList, I expect two things:
1. The Route will be updated with the search term I just clicked (working)
2. The Giphy component shows up the new gifs relating to the search term from the route (not working)
Actually I don't know what the problem is.

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

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 /> }

Resources