I have a navbar for all pages. I want to make a cart in it, however, when I go to the internal product page, the props that I transmit are not displayed.
Why is this happening ?
I think this is my problem React router v4 not working with Redux
but how i can implement this ?
What do you think ?
App.js
import React, {Component} from 'react';
import {Container} from 'reactstrap';
import {
BrowserRouter,
Route,
Switch
} from "react-router-dom";
import './App.css';
import NavbarMenu from './components/navbar'
import Main from './components/main';
import Good from './components/good';
class App extends Component {
render() {
return (
<BrowserRouter>
<div className="App">
<NavbarMenu/>
<Container>
<Switch>
<Route path="/" exact component={Main}/>
<Route path="/good/:id" component={Good}/>
</Switch>
</Container>
</div>
</BrowserRouter>
);
}
}
export default App;
navbar
import React from 'react';
import {
Collapse,
Navbar,
NavbarToggler,
NavbarBrand,
Nav,
UncontrolledDropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
Button
} from 'reactstrap';
import {withRouter} from 'react-router-dom';
import connect from 'react-redux/es/connect/connect';
import {getCart} from '../../redux/actions/cartAction';
class NavbarMenu extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
isOpen: false
};
}
toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}
render() {
console.log(this.props)
const cartLength = this.props.cart.length;
const cartItems = this.props.cart.map(e => {
return <div key={e.id} style={{marginBottom: '20px'}}>
<DropdownItem
style={{display: 'inline-block', width: 'auto'}}
onClick={() => {
this.props.history.push('/good/' + e.id)
}}>
{e.name}
</DropdownItem>
<Button
style={{display: 'inline-block', float: 'right', marginRight: '20px'}}
color="danger"
>X</Button>
</div>
});
return (
<Navbar
color="light"
light expand="md"
style={{marginBottom: '20px'}}
>
<NavbarBrand
style={{cursor: 'pointer'}}
onClick={() => {
this.props.history.push('/')
}}
>
Shop
</NavbarBrand>
<NavbarToggler onClick={this.toggle}/>
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className="ml-auto" navbar>
<UncontrolledDropdown nav inNavbar>
<DropdownToggle nav caret>
Cart: {cartLength} items
</DropdownToggle>
<DropdownMenu right style={{width: '300px'}}>
{cartItems}
<DropdownItem divider/>
</DropdownMenu>
</UncontrolledDropdown>
</Nav>
</Collapse>
</Navbar>
);
}
}
const mapStateToProps = state => ({
cart: state.cart.cart
});
export default withRouter(connect(mapStateToProps, {getCart})(NavbarMenu));
Based on the prints you gave, you are opening the item on a diferent window, because of that, the variables on the session in the window are not passed.
One solution you could use is to save pieces of your store that you will need later in the browser localStorage.
You can do that using this by using the Redux subscribe function.
A example could be:
localStorage.js
export const loadState = () => {
try {
const serializedState = localStorage.getItem('state');
if (serializedState === null) return undefined;
return JSON.parse(serializedState);
} catch (err) { return undefined; }
};
export const saveState = (state) => {
try {
const serializedState = JSON.stringify(state);
} catch (err) {
// errors
}
}
And in the redux store you can put:
import { loadState, saveState } from './localStorage';
const persistedState = loadState();
const store = createStore(persistedState);
store.subscribe(() => saveState(store.getState()));
Source: https://egghead.io/lessons/javascript-redux-persisting-the-state-to-the-local-storage
I solved my problem by adding this code
componentDidMount() {
this.props.getCart();
}
Related
I always work with functional components as they give me more flexibility and i can use hooks with them. I have a drop down menu functionality that is coded using the class component. Now i need to convert this to a functional component as i need to do some backend work for some of the menu buttons, such as logout and name and so on.
I tried to convert it my self but it ended with always keeping to Modal open and giving me an error that reads " this.props.toggle is not a function at Dropdown.toggle" and it slowed down my website incredibly.
How can i make the below code equivalent to a functional component?
Class Component:
import React, { Component } from "react";
import PropTypes from 'prop-types';
import {
Dropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
} from "reactstrap";
import { withRouter, Link } from "react-router-dom";
//i18n
import { withTranslation } from "react-i18next";
import { connect } from "react-redux";
class ProfileMenu extends Component {
constructor(props) {
super(props)
this.state = {
menu: false,
name: "Admin",
}
this.toggle = this.toggle.bind(this)
}
toggle() {
this.setState(prevState => ({
menu: !prevState.menu,
}))
}
render() {
return (
<React.Fragment>
<Dropdown
isOpen={this.state.menu}
toggle={this.toggle}
className="d-inline-block"
>
<DropdownToggle
className="btn header-item"
id="page-header-user-dropdown"
tag="button"
>
<span className="d-none d-xl-inline-block ms-1">
{this.state.name}
</span>
<i className="mdi mdi-chevron-down d-none d-xl-inline-block" />
</DropdownToggle>
<DropdownMenu className="dropdown-menu-end">
<DropdownItem tag="a" href="/">
Dashboard
</DropdownItem>
<div className="dropdown-divider" />
<Link to="/logout" className="dropdown-item">
<i className="bx bx-power-off font-size-16 align-middle me-1 text-danger" />
<span>{this.props.t("Logout")}</span>
</Link>
</DropdownMenu>
</Dropdown>
</React.Fragment>
)
}
}
ProfileMenu.propTypes = {
t: PropTypes.any,
success: PropTypes.string
}
const mapStateToProps = state => {
const { success } = state.Profile
return { success }
}
export default withRouter(
connect(mapStateToProps, {})(withTranslation()(ProfileMenu))
)
Below code is how i tried to convert it
import React, { Component, useState, useEffect } from "react";
import PropTypes from 'prop-types';
import {
Dropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
} from "reactstrap";
import { withRouter, Link, useHistory } from "react-router-dom";
import firebase from "../../../firebase"
function ProfileMenu(){
const [isOpen, setIsOpen] = useState(false);
const history = useHistory();
const logout = () => {
firebase.auth().signOut().then(function() {
console.log("logged out")
history.push("/login");
}).catch(function(error) {
// An error happened.
console.log("didnt logout")
});
}
const openModal = () => {
setIsOpen(true);
};
const closeModal = () => {
setIsOpen(false);
};
return (
<React.Fragment>
<Dropdown
isOpen={isOpen}
onClick={openModal}
toggle={""}
className="d-inline-block"
>
<DropdownToggle
// onClick={openModal}
className="btn header-item"
id="page-header-user-dropdown"
tag="button"
>
<span className="d-none d-xl-inline-block ms-1">
{/* {this.state.name} */}
</span>
<i className="mdi mdi-chevron-down d-none d-xl-inline-block" />
</DropdownToggle>
<DropdownMenu className="dropdown-menu-end">
<DropdownItem tag="a" href="/">
Dashboard
</DropdownItem>
<div className="dropdown-divider" />
<button className="dropdown-item" onClick={logout}>
<i className="bx bx-power-off font-size-16 align-middle me-1 text-danger" />
<span>Logout</span>
</button>
</DropdownMenu>
</Dropdown>
</React.Fragment>
)
}
export default ProfileMenu
const ProfileMenu = (props) => {
const [name, setName] = useState('aaa'); // define and initialize name state
....
let myName = name; // call name state
....
let toggle = props.toggle; //call toggle props
}
I hope this will be helpful for you. Thanks.
You need to pass the toggle as function, you are instead passing string, also you can directly toggle state value, it will make the component re-render, try the following code and let me know if this helps.
Just for better visibility I am removing the class names, so that code looks clean.
You can also use the useContext instead of using redux.
import React, { useState } from "react";
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from "reactstrap";
import { withRouter, useHistory } from "react-router-dom";
import firebase from "../../../firebase";
type Prop = {
name: string;
};
const ProfileMenu: React.FC<Prop> = ({ name }) => {
const [menu, setMenu] = useState(false);
const history = useHistory();
const logout = () => {
firebase
.auth()
.signOut()
.then(function () {
history.push("/login");
})
.catch(function (error) {
console.log("didnt logout");
});
};
return (
<Dropdown isOpen={menu} toggle={() => setMenu(!menu)} >
<DropdownToggle >
<span >{name}</span>
<i className="mdi mdi-chevron-down d-none d-xl-inline-block" />
</DropdownToggle>
<DropdownMenu >
<DropdownItem tag="a" href="/">
Dashboard
</DropdownItem>
<div className="dropdown-divider" />
<button onClick={logout}>
<i className="bx bx-power-off font-size-16 align-middle me-1 text-danger" />
<span>Logout</span>
</button>
</DropdownMenu>
</Dropdown>
);
};
export default withRouter(ProfileMenu);
i am trying create a transition screen from one page to the other
function MainPage(){
return (
<div>
{pagestate.navigating == "true" ? (
<FadeIn>
<div className="d-flex justify-content-center align-items-center">
<h1>signing you in ....</h1>
<Link to="/" color="black" >sign in</Link>
{pagestate.loading =="false" ? (
<Lottie options={defaultOptions} height={120} width={120} />
) : (
<Lottie options={defaultOptions2} height={220} width={120} />
)}
</div>
</FadeIn>
) : (
<div>
<h1>hello world</h1>
<Link to="/" color="black" >sign in</Link>
</div>
)}
The code works fine but I want it to navigate to /page2 when pagestate.loading = "false". I was able to achieve the page navigation with using
const history = useHistory()
then call navigation like
history.push('/page2')
I tried couple of method but could not get it work inside the transition logic.
How can I incorporate to the navigation into a new page after loading state has been changed to false in the transition logic above?
Encountered that a couple of days ago, i found a solution to it but it’s kinda weird,i’ve done it using redux, i’ve made a Link Component Called LinkWithAnimation,created a reducer as RouteReducer which will store current transition state, 2 states:
First one is For Transitioning In.
Second one is For Transitioning Out.
Wrapped my application with a div and passed the Transition that’s stored in redux, everytime LinkWithAnimation Is clicked This Is What Happens:
Dispatch An Action For Transitioning In
Wait(Delay) Till the Transition Has Finished(Depending On The Duration Of It)
Dispatch An Action for Transitioning Out.
And then push the new path using History API.
Note: Make Use Of Redux Thunk.
ActionTypes.js
export const ActionsType = {
...otherActions,
ANIMATION_IN: "animation-in",
ANIMATION_OUT: "animation-out",
};
ActionsCreator.js
import { ActionsType } from "./ActionsType.js";
import { history } from "../index.js";
export const ActionsCreator = {
...otherActionCreators,
userLogout: () => ({ type: ActionsType.LOGOUT }),
animateIn: () => ({ type: ActionsType.ANIMATION_IN }),
animateOut: () => ({ type: ActionsType.ANIMATION_OUT }),
pageTransition: (duration, path) => {
return async (dispatch) => {
const delay = async () => {
return new Promise((resolve) => setTimeout(resolve, duration));
};
dispatch(ActionsCreator.animateOut());
await delay();
dispatch(ActionsCreator.animateIn());
history.push(path);
};
},
};
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { Router } from "react-router-dom";
import { createBrowserHistory } from "history";
export const history = createBrowserHistory();
ReactDOM.render(
<Router history={history}>
<React.StrictMode>
<App />
</React.StrictMode>
</Router>,
document.getElementById("root")
);
LinkWithAnimation.js
import React, { useRef, useEffect } from "react";
import { Link } from "react-router-dom";
import { ActionsCreator } from "../actions/ActionsCreator.js";
import { connect } from "react-redux";
const LinkWithAnimation = ({
className,
additionalFunction,
PageTransitioning,
to,
children,
style,
component,
ReactTo,
disabled,
}) => {
const LinkRef = useRef();
// This Effect To Handle Page Transition Once The User Is Signed In
useEffect(() => {
if (ReactTo === true) {
LinkRef.current.click();
}
}, [ReactTo]);
const componentProp =
component !== undefined
? {
component: component,
}
: {};
return (
<>
<Link
onClick={async () => {
if (disabled) return;
PageTransitioning(230, to);
if (additionalFunction !== undefined) {
additionalFunction();
}
}}
ref={LinkRef}
className={className}
style={{ ...style }}
{...componentProp}
>
{children}
</Link>
</>
);
};
const mapDispatchToProps = (dispatch) => ({
PageTransitioning: (duration, path) => {
dispatch(ActionsCreator.pageTransition(duration, path));
},
});
export default connect(null, mapDispatchToProps)(LinkWithAnimation);
Main.js
import React, { Fragment } from "react";
import { Switch, Route } from "react-router-dom";
import { connect } from "react-redux";
import Homepage from "./Homepage/Homepage.js";
import Signup from "./Signup/Signup.js";
import UserInterface from "./UserInterface/UserInterface.js";
import { SignIn } from "./SignIn/SignIn.js";
import { useRouteTransitionScroll } from "../hooks/useRouteTransitionScroll.js";
const Main = ({ keyframe }) => {
useRouteTransitionScroll({
from: "/signup",
to: "/home",
scroll_y: 650,
});
return (
<Switch component={Fragment}>
<div
style={{
animationName: keyframe,
animationDuration: "250ms",
animationTimingFunction: "linear",
}}
>
<Route path="/mainpage">
<UserInterface />
</Route>
<Route path="/home">
<Homepage />
</Route>
<Route path="/signin">
<SignIn />
</Route>
<Route path="/signup">
<Signup />
</Route>
</div>
</Switch>
);
};
const mapStateToProps = (state) => ({
keyframe: state.Route.animationName,
});
export default connect(mapStateToProps)(Main);
I am working on putting together a webpage with routing and I am having trouble getting redux to work. My goal is to send a GET response to the reducer but just to test the setup right now my goal is to send true. I can retrieve data from the redux store but I can't seem to send it and I am unsure where I might be going wrong. Here is what is supposed to happen:
Auth is checked in login or signup
App.js is wrapped in a provider
User can go to Cart.js and by clicking a button dispatch the value true
The can navigate to Menu.js and should be able to console.log the new value from the reducer
My problem: I can't seem to actually dispatch the true value. Nothing breaks but when I go to the Menu page, the console log shows the initial state of the reducer.
This has worked for me before in React Native. I'm wondering if I should be setting this up differently? or if authentication is messing things up?
Below is a sample of my code:
App.js
import React, { Component } from 'react';
import {
Route,
BrowserRouter as Router,
Switch,
Redirect,
} from "react-router-dom";
import Home from './pages/Home';
import Signup from './pages/Signup';
import Login from './pages/Login';
import Menus from './pages/Menus';
import Carts from './pages/Carts';
import Orders from './pages/Orders';
import Land from './pages/Land';
import { auth } from './services/firebase';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import ourReducer from './store/reducer';
const store = createStore(ourReducer);
global.api = 'https://pitapal.metis-data.site'
//global.api = 'http://localhost:3008';
function PrivateRoute({ component: Component, authenticated, ...rest }) {
return (
<Route
{...rest}
render={(props) => authenticated === true
? <Component {...props} />
: <Redirect to={{ pathname: '/login', state: { from: props.location } }} />}
/>
)
}
function PublicRoute({ component: Component, authenticated, ...rest }) {
return (
<Route
{...rest}
render={(props) => authenticated === false
? <Component {...props} />
: <Redirect to='/home' />}
/>
)
}
class App extends Component {
constructor() {
super();
this.state = {
authenticated: false,
loading: true,
};
}
componentDidMount() {
auth().onAuthStateChanged((user) => {
if (user) {
this.setState({
authenticated: true,
loading: false,
});
} else {
this.setState({
authenticated: false,
loading: false,
});
}
})
}
render() {
return this.state.loading === true ? <h2>Loading...</h2> : (
<Provider store={ store }>
<Router>
<Switch>
<Route exact path="/" component={Signup}></Route>
<PrivateRoute path="/home" authenticated={this.state.authenticated} component={Home}></PrivateRoute>
<PrivateRoute path="/menus" authenticated={this.state.authenticated} component={Menus}></PrivateRoute>
<PrivateRoute path="/carts" authenticated={this.state.authenticated} component={Carts}></PrivateRoute>
<PrivateRoute path="/order" authenticated={this.state.authenticated} component={Orders}></PrivateRoute>
<PublicRoute path="/signup" authenticated={this.state.authenticated} component={Signup}></PublicRoute>
<PublicRoute path="/login" authenticated={this.state.authenticated} component={Login}></PublicRoute>
</Switch>
</Router>
</Provider>
);
}
}
export default App;
Reducer.js
import { combineReducers } from 'redux';
const INITIAL_STATE = {
carts: 'nothing'
};
const ourReducer = (state = INITIAL_STATE, action) => {
const newState = { ...state };
switch (action.type) {
case "CARTS":
return {
...state,
carts: action.value
}
break;
}
return newState;
};
export default combineReducers({
reducer: ourReducer,
});
Carts.js
class Carts extends Component {
render() {
return (
<Container>
<Button onClick={()=>this.props.setCart(true)}>sendToRedux</Button>
</Container>
);
}
}
const mapStateToProps = (state) => {
const { reducer } = state
return { reducer }
};
const mapDispachToProps = dispatch => {
return {
setCart: (y) => dispatch({ type: "CARTS", value: y })
};
}
export default connect(mapStateToProps,
mapDispachToProps
)(Carts);
Menu.js
import React, { Component } from 'react';
import Header from "../components/Header";
import MenuItem from '../components/MenuItem';
import classes from './menus.module.css'
import { auth, db } from "../services/firebase";
import { Container, Col, Row } from 'react-bootstrap';
import { connect } from 'react-redux';
class Menu extends Component {
render() {
console.log('my carts data:', this.props.reducer.carts);
}
return (
<Container>
welcome to menu
</Container>
);
}
}
const mapStateToProps = (state) => {
const { reducer } = state
return { reducer }
};
export default connect(mapStateToProps)(Menu);
EDIT:
here is my screenshot from Redux Devtools, this means the dispatch is definitely being sent correct?
So it seems the issue is that when I navigate a page using my header component, state gets reloaded, the entire app is relaoded. Wondering if someone knows what it might be. Below is my header component:
Header.js
import { Link } from 'react-router-dom';
import { auth } from '../services/firebase';
//import "./header.css";
import React, { Component } from 'react'
import { Navbar, Nav, NavItem, NavDropdown, Form, FormControl, Button } from 'react-bootstrap'
// import {Link} from 'react-router-dom'
//import classes from './navbar.module.css';
import 'bootstrap/dist/css/bootstrap.min.css';
class Header extends Component {
render() {
return (
<div>
{auth().currentUser
?
<Navbar className="fixed-top" collapseOnSelect expand="lg" style={{ backgroundColor: '#485671' }} variant="dark">
<Navbar.Brand href="#home">PitaPal</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Nav.Link href="/login">Home</Nav.Link>
<Nav.Link href="/menus">Manage my Menus</Nav.Link>
</Nav>
<Nav>
<Button onClick={() => auth().signOut()} variant="outline-success">Sign Out</Button>
</Nav>
</Navbar.Collapse>
</Navbar>
:
<Navbar className="fixed-top" collapseOnSelect expand="lg" style={{ backgroundColor: '#485671' }} variant="dark">
<Navbar.Brand href="#home">PitaPal</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Nav.Link href="/login">Login</Nav.Link>
<Nav.Link href="/signup">Sign Up</Nav.Link>
</Nav>
<Nav>
<Nav.Link href="contact">Contact Us</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
}
</div>
)
}
}
export default Header;
Issue
Your Header is using plain anchor tags to issue navigation to your route pages which has the side-effect of also doing a page load.
Solution
Use the react-router-dom Link component for linking.
Either specify the as prop of the Nav.Link or use Link
<Nav.Link as={Link} to="/login">Home</Nav.Link>
or
<Link to="/login">Home</Link>
class Header extends Component {
render() {
return (
<div>
{auth().currentUser
?
<Navbar className="fixed-top" collapseOnSelect expand="lg" style={{ backgroundColor: '#485671' }} variant="dark">
<Navbar.Brand href="#home">PitaPal</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Link to="/login">Home</Link>
<Link to="/menus">Manage my Menus</Link>
</Nav>
<Nav>
<Button onClick={() => auth().signOut()} variant="outline-success">Sign Out</Button>
</Nav>
</Navbar.Collapse>
</Navbar>
:
<Navbar className="fixed-top" collapseOnSelect expand="lg" style={{ backgroundColor: '#485671' }} variant="dark">
<Navbar.Brand href="#home">PitaPal</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Link to="/login">Login</Link>
<Link to="/signup">Sign Up</Link>
</Nav>
<Nav>
<Link to="contact">Contact Us</Link>
</Nav>
</Navbar.Collapse>
</Navbar>
}
</div>
)
}
}
I'm having an issue with the code
TypeError: Cannot set property 'props' of undefined
i think i did everything right. I even referenced
react cannot set property of props of undefined
and
React-router: TypeError: Cannot set property 'props' of undefined
was unable to figure out the error.
import React from 'react';
import {BrowserRouter as Router, Route, Link} from "react-router-dom";
import signUp from './signUp';
import signIn from './signIn';
import Users from './Users';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import IconButton from '#material-ui/core/IconButton';
import MenuIcon from '#material-ui/icons/Menu';
import Button from '#material-ui/core/Button';
import {withStyles} from '#material-ui/core';
import Dashboard from './dashBoard';
import { connect } from 'react-redux';
import { createBrowserHistory } from 'history';
import PropTypes, { func, bool, string} from 'prop-types';
export const history = createBrowserHistory({forceRefresh: true});
const styles = {
// This group of buttons will be aligned to the right
rightToolbar: {
color: '#fff',
textDecoration: 'none',
a: {
color: '#fff'
}
},
rightt: {
marginLeft: 'auto',
marginRight: 24
},
root: {
flexGrow: 1
},
menuButton: {
marginRight: 16,
marginLeft: -12
}
};
const logout = (e) => {
e.preventDefault();
localStorage.removeItem('JWT');
};
const Navbar = ({classes, props}) => (
<Router history={history}>
<div className={classes.root}>
<AppBar position="static" className={classes.navbar}>
<Toolbar>
<IconButton color="inherit" aria-label="Menu">
<MenuIcon/>
</IconButton>
<Typography variant="h6" color="inherit">
Eli App
</Typography>
<Typography classcolor="inherit" className={classes.rightt}>
{!props.token && (
<Button>
<Link to="/signUp" className={classes.rightToolbar} >
Sign Up
</Link>
</Button>
)}
<Button>
<Link to="/users" className={classes.rightToolbar}>
Users
</Link>
</Button>
<Button>
<Link to="/dashboard" className={classes.rightToolbar}>
Dashboard
</Link>
</Button>
<Button
onClick={logout}
>
<Link className={classes.rightToolbar} to={'/'}>
LogOut
</Link>
</Button>
</Typography>
</Toolbar>
</AppBar>
<Route path="/signUp" component={signUp}/>
<Route path="/signIn" component={signIn}/>
<Route path="/users" component={Users}/>
<Route path="/dashboard" component={Dashboard}/>
<Route path="/signOut"/>
</div>
</Router>
);
const mapStateToProps = (state) => ({
token: state.user.getToken
})
const mapDispatchToProps = (dispatch) => ({
// logIn: (user) => dispatch(logIn(user))
});
Navbar.propTypes = {
token:PropTypes.string,
}
// export default withStyles(styles)(Navbar);
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles))(Navbar);
updated
Th values you are deconstructing in your function's arguments are already your props, if you want to access your token, you can do the following :
const Navbar = ({classes, token}) => ( //classes and token are INSIDE your props
In the render function :
{!token && (
It seems like the problem could also come from your export :
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles))(Navbar);
You should use compose to use multiple HOC together :
import { compose } from 'redux'
//....
export default compose(connect(mapStateToProps, mapDispatchToProps), withStyles(styles))(Navbar);
You may also experience some unexpected behavior when using a stateless function with redux, I suggest also using the solution from #Shalini Sentiya.
Try to using the navbar react class instead of the function
class Navbar extends Component {
constructor(props){
super(props);
}
render() {
const { token } = this.props;
return (
<Router history={history}>
// your code
{!token && (
<Button>
<Link to="/signUp" className={classes.rightToolbar} >
Sign Up
</Link>
</Button>
)}
// your code
</Router>
);
}
}
const mapStateToProps = (state) => ({
token: state.user.getToken
})
const mapDispatchToProps = (dispatch) => ({
// logIn: (user) => dispatch(logIn(user))
});
Navbar.propTypes = {
token:PropTypes.string,
}
// export default withStyles(styles)(Navbar);
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles))(Navbar);
This is a strange issue, but when I try to do a redirect using a link, nothing happens, just the URL changes. But when I refresh the browser the component gets rendered. What am I doing wrong?
My nav.js
import React from 'react';
import {Navbar, Nav, NavItem, Modal, Button, FormControl} from 'react-bootstrap';
import {BrowserRouter, Link, Route, Switch} from 'react-router-dom';
import {auth} from '../firebase';
import Questions from './questions';
import {About} from './about';
import {Home} from './home';
import {LinkContainer} from 'react-router-bootstrap';
import Question from "./question";
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
this.openLogin = this.openLogin.bind(this);
this.handleClose = this.handleClose.bind(this);
}
componentDidMount() {
auth.onAuthStateChanged((user) => {
if (user) {
this.setState({
user: user
}, function () {
this.props.checkUserState(this.state.user)
});
}
});
}
logout() {
auth.signOut()
.then(() => {
this.setState({
user: null
}, function () {
this.props.checkUserState(this.state.user)
});
});
}
login() {
var email = document.getElementById('email').value;
var password = document.getElementById('password').value;
auth.signInWithEmailAndPassword(email, password)
.then(result => {
const user = result.user;
this.setState({
user: user,
},
function () {
this.props.checkUserState(this.state.user)
});
document.getElementById('close').click();
document.getElementById('questions').click();
}
).catch(e => console.log(e));
}
openLogin() {
this.setState({show: true});
}
handleClose() {
this.setState({show: false});
}
render() {
return (
<React.Fragment>
<BrowserRouter>
<React.Fragment>
<Navbar>
<Navbar.Header>
<Navbar.Brand>
<Link id='home' to="/">UczIchApp</Link>
</Navbar.Brand>
</Navbar.Header>
<Nav>
<LinkContainer id='about' to='/about'>
<NavItem>O nas</NavItem>
</LinkContainer>
{
this.state.user ?
<React.Fragment>
<LinkContainer id="questions" to='/questions'>
<NavItem>Zadania</NavItem>
</LinkContainer>
<NavItem onClick={this.logout}>Wyloguj się</NavItem>
</React.Fragment>
:
<NavItem onClick={this.openLogin}>Zaloguj się</NavItem>
}
</Nav>
</Navbar>
<Switch>
<Route exact path="/about" component={About}/>
<Route exact path="/questions" component={Questions}/>
<Route exact path="/" component={Home}/>
<Route path='/question/:id' component={Question}/>
</Switch>
</React.Fragment>
</BrowserRouter>
<Modal
show={this.state.show}
onHide={this.handleClose}>
<Modal.Header
closeButton>
<Modal.Title> Modal
heading </Modal.Title>
</Modal.Header>
<Modal.Body>
<form>
<FormControl
id="email"
type="email"
label="Email address"
placeholder="Enter email"/>
<FormControl id="password" label="Password" type="password"/>
<Button onClick={this.login}>Zaloguj</Button>
</form>
</Modal.Body>
<Modal.Footer>
<Button id="close" onClick={this.handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</React.Fragment>
)
}
}
export default Navigation;
My Questions.js
import React from 'react';
import firebase from 'firebase';
// import {Button} from 'react-bootstrap';
import {BrowserRouter as Router, Link, Route} from 'react-router-dom';
import Question from './question';
class Questions extends React.Component {
constructor(props) {
super(props);
this.state = {
currentItem: '',
username: '',
questions: []
};
}
componentDidMount() {
const questionsRef = firebase.database().ref('Works').orderByChild('available').equalTo(true).limitToFirst(10);
questionsRef.on('value', (snapshot) => {
let questions = snapshot.val();
let newState = [];
for (let question in questions) {
newState.push({
id: question,
category: questions[question].category,
level: questions[question].level,
pointAmount: questions[question].pointAmount,
pointBoost: questions[question].pointBoost,
photoURL: questions[question].photoURL,
});
}
this.setState({
questions: newState
});
});
}
render() {
return (
<section id='loopContainer' className='display-question'>
<div className='wrapper'>
<ul style={{listStyleType: 'none'}}>
{
this.state.questions.map(question => {
return (
<li key={question.id}>
<h3>Kategoria: {question.category}</h3>
<p>Poziom: {question.level}</p>
<p>Punkty: {question.pointAmount + question.pointBoost}</p>
<img alt='' style={{width: '20%'}} src={question.photoURL}/>
<Router>
<React.Fragment>
<Link to={`/question/${question.id}`}
style={{display: 'block', margin: 'auto'}}>Rozwiaz to zadanie
</Link>
</React.Fragment>
</Router>
</li>
)
})
}
</ul>
</div>
</section>
)
}
}
export default Questions;
My Question.js
import React from 'react';
import firebase from 'firebase';
export default class Question extends React.Component {
constructor(p) {
super(p);
this.state = {
currentItem: '',
username: '',
questions: []
};
}
componentDidMount() {
const questionsRef = firebase.database().ref('Works').orderByChild('firebaseKey').equalTo(this.props.match.params.id);
questionsRef.on('value', (snapshot) => {
let questions = snapshot.val();
let newState = [];
for (let question in questions) {
newState.push({
id: question,
category: questions[question].category,
level: questions[question].level,
pointAmount: questions[question].pointAmount,
pointBoost: questions[question].pointBoost,
photoURL: questions[question].photoURL,
});
}
console.log(newState);
this.setState({
questions: newState
});
});
}
render() {
return (
this.state.questions.map(question => {
return (
<section key={question.id} className='display-question'>
<div className='wrapper'>
<h3>Kategoria: {question.category}</h3>
<p>Poziom: {question.level}</p>
<p>Punkty: {question.pointAmount + question.pointBoost}</p>
<img alt='' style={{width: '80%'}} src={question.photoURL}/>
</div>
</section>
)
})
)
}
}
What I'm trying to do. I'm trying to get a Question component rendered, when a link rendered with the Questions component is clicked. Based on the id, the Question component will differ.
This is the Questions component example (It's a list):
When I click the link under an image it changes the url, like this: http://localhost:3000/question/-LDvDwsIrf_SCwSinpMa, but nothing else happens. I have to manually refresh the page to get the component.
This is the single Question component being rendered
What am I missing?
You define multiple Router instances. However, there should be exactly one Router instance. The Router is typically placed pretty much "top-level" within your App / Main / Root component.