after i login in user page i try to refresh page -it throw an error ?
here is my dashboard, in my application is already running, it works fine, but already on the user page when i try to refresh it i got the error
here is my code
https://codesandbox.io/s/serene-https-oi6hw
the error is :
Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an
array instead.
import { useSelector } from "react-redux";
//react-redux actions
import { Userinfo } from "../auth/actions/userActions";
import { logoutUser } from "../auth/actions/userActions";
//history
import { useHistory } from "react-router-dom";
const Dashboard = () => {
const history = useHistory();
const selectUser = (state) => state.user;
const user = useSelector(selectUser);
return (
<div>
<div
style={{
position: "absolute",
top: 0,
left: 0,
backgroundColor: "transparent",
width: "100%",
padding: "15px",
display: "flex",
justifyContent: "flex-start"
}}
>
<Avatar image={Logo} />
</div>
<StyledFromArea bg={colors.dark2}>
<StyledTitle size={65}>Hello, {user}</StyledTitle>
<Userinfo />
<ButtonGroup>
<StyledButton to="#" onClick={() => logoutUser(history)}>
Logout
</StyledButton>
</ButtonGroup>
</StyledFromArea>
</div>
);
};
export default Dashboard;
Here is the problem
<StyledTitle size={65}>Hello, {user}</StyledTitle>
user is an object.
Try Hello, {user?.email} or the property you wanted to display
Try this:
import { useSelector } from "react-redux";
//react-redux actions
import { Userinfo } from "../auth/actions/userActions";
import { logoutUser } from "../auth/actions/userActions";
//history
import { useHistory } from "react-router-dom";
const Dashboard = () => {
const history = useHistory();
const selectUser = (state) => state.user;
const user = useSelector(selectUser);
return (
<div>
<Avatar image={Logo} />
<StyledFromArea bg={colors.dark2}>
<StyledTitle size={65}>Hello, {user}</StyledTitle>
<Userinfo />
<ButtonGroup>
<StyledButton to="#" onClick={() => logoutUser(history)}>
Logout
</StyledButton>
</ButtonGroup>
</StyledFromArea>
</div>
);
};
export default Dashboard;
Related
I installed a package from the mui website and for some reason when I import "#mui/styles" I get an a error coming from my browser. This is where I installed #mui/styles from:
https://mui.com/system/styles/basics/
My browser gives me 5 errors when I run my react app and it doesn't load up when I type "npm start". Here's a screenshot of the errors:
Here is the Javascript code:
import React, { useState, useEffect } from 'react';
import './App.css';
import Post from './Post';
import { db } from './firebase';
import { makeStyles } from '#mui/styles';
import Modal from '#mui/material/Modal';
import { Button } from '#mui/material';
function getModalStyle() {
const top = 50;
const left = 50;
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)` ,
};
}
const useStyles = makeStyles((theme) => ({
paper: {
position: 'absolute',
width: 400,
backgroundColor: theme.palette.background.paper,
border: '2px solid #080',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
function App() {
const classes = useStyles();
const [modalStyle] = React.useState(getModalStyle);
const [posts, setPosts] = useState([]);
const [open, setOpen] = useState(false);
//useEffect: Runs a piece of code based on a specific condition
useEffect(() => {
//this is where the code runs
db.collection('posts').onSnapshot(snapshot => {
//Everytime a new post is added, this line of code activates
setPosts(snapshot.docs.map(doc => ({
id: doc.id,
post: doc.data()
})))
}) //"posts" inside of firebase also everytime a document gets modified inside of post it takes a screenshot
}, [] ); //conditions go here and there just variables
return (
<div className="App">
<Modal
open={open}
onClose={() => setOpen(false)}
>
<div style={modalStyle} className={classes.paper}>
<h2>I am a modal</h2>
</div>
</Modal>
<div className="app__header">
<img
className="app__headerImage"
src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
alt="instagram_text"
/>
</div>
<Button></Button>
<h1>Hello clever programmers let's build a react app!!!</h1>
{
posts.map(({id, post}) => (
<Post key={id} username={post.username} caption={post.caption} imageUrl={post.imageUrl} />
))
}
</div>
);
}
export default App;
'theme' in makeStyles is empty.
const useStyles = makeStyles((theme) => ({
paper: {
position: 'absolute',
width: 400,
backgroundColor: theme.palette.background.paper,
border: '2px solid #080',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
Instead, you should get theme by:
import { createTheme } from '#mui/material/styles'
const theme = createTheme()
complete code:
import React, { useState, useEffect } from 'react';
import './App.css';
import Post from './Post';
import { db } from './firebase';
import { makeStyles } from '#mui/styles';
import { createTheme } from '#mui/material/styles'
import Modal from '#mui/material/Modal';
import { Button } from '#mui/material';
function getModalStyle() {
const top = 50;
const left = 50;
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)` ,
};
}
const theme = createTheme()
const useStyles = makeStyles(() => ({
paper: {
position: 'absolute',
width: 400,
backgroundColor: theme.palette.background.paper,
border: '2px solid #080',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
function App() {
const classes = useStyles();
const [modalStyle] = React.useState(getModalStyle);
const [posts, setPosts] = useState([]);
const [open, setOpen] = useState(false);
//useEffect: Runs a piece of code based on a specific condition
useEffect(() => {
//this is where the code runs
db.collection('posts').onSnapshot(snapshot => {
//Everytime a new post is added, this line of code activates
setPosts(snapshot.docs.map(doc => ({
id: doc.id,
post: doc.data()
})))
}) //"posts" inside of firebase also everytime a document gets modified inside of post it takes a screenshot
}, [] ); //conditions go here and there just variables
return (
<div className="App">
<Modal
open={open}
onClose={() => setOpen(false)}
>
<div style={modalStyle} className={classes.paper}>
<h2>I am a modal</h2>
</div>
</Modal>
<div className="app__header">
<img
className="app__headerImage"
src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
alt="instagram_text"
/>
</div>
<Button></Button>
<h1>Hello clever programmers let's build a react app!!!</h1>
{
posts.map(({id, post}) => (
<Post key={id} username={post.username} caption={post.caption} imageUrl={post.imageUrl} />
))
}
</div>
);
}
export default App;
I am experiencing so really strange behaviour in this personal project i am doing. In short it is a recipe website, with buttons at the top that direct to different pages that ultimately query firebase and pull down a query.
Index.js File - Queries Firestore passes props to FoodCard.js to Render a list of all recipes
breakfast.js - Queries Firestore with a filter and passes same props down (with different results) to FoodCard.js
Behaviour
When i click on Breakfast JS, it brings up my list of filtered results correctly, however when i click my Next Link "Welcome to the Family Heirloom" to return to the index the first click doesnt respond, then the second click returns home, but with the filtered breakfast result concatonated with all the original results (effectively causing duplicates)Index on First Render
Breakfast filter successful
index with the now duplicate pancake result
I have messed about wondering if useEffect is not getting triggered which you may see in the code, but that didnt seem to work, so at a loss
Index.js
import { React, useEffect, useState } from 'react'
import { useRouter } from 'next/dist/client/router'
import { useTheme } from '#mui/material/styles'
import { makeStyles } from '#mui/styles'
import Modal from '#mui/material/Modal'
import FoodCard from '../src/ui/FoodCard.js'
import firebase from '../firebase/initFirebase'
import Box from '#mui/material/Box'
import Typography from '#mui/material/Typography'
const useStyles = makeStyles(theme => ({
mainContainer: {
}
}))
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 400,
bgcolor: 'red',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
export default function CookBook() {
const router = useRouter();
const { id } = router.query;
const classes = useStyles()
const theme = useTheme()
const [loading, setLoading] = useState(true);
const [recipes, setRecipes] = useState([]);
const [open, setOpen] = useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
useEffect(() => {
const getRecipesFromFirebase = [];
const subscriber = firebase.firestore()
.collection("recipes")
.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(querySnapshot)
getRecipesFromFirebase.push({
...doc.data(), //spread operator
key: doc.id, // `id` given to us by Firebase
});
});
setRecipes(getRecipesFromFirebase);
console.log(recipes);
setLoading(false);
});
return () => subscriber();
}, [loading, router.events]); // empty dependencies array => useEffect only called once
if (loading) {
return (
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<Typography id="modal-modal-title" variant="h6" component="h2">
Loading Data
</Typography>
</Box>
</Modal>)
}
return (
<FoodCard recipes={recipes} />
)
}
_app.js
import * as React from 'react';
import PropTypes from 'prop-types';
import Head from 'next/head';
import { ThemeProvider } from '#mui/material/styles';
import CssBaseline from '#mui/material/CssBaseline';
import { CacheProvider } from '#emotion/react';
import theme from '../src//ui/theme';
import createEmotionCache from '../src/createEmotionCache';
import Header from '../src/ui/Header';
import Grid from '#mui/material/Grid'
import Typography from '#mui/material/Typography'
import { Link as MUILink } from '#mui/material/'
import NextLink from 'next/link'
// Client-side cache, shared for the whole session of the user in the browser.
const clientSideEmotionCache = createEmotionCache();
export default function MyApp(props) {
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
return (
<CacheProvider value={emotionCache}>
<Head>
<meta name="viewport" content="initial-scale=1, width=device-width" />
</Head>
<ThemeProvider theme={theme}>
<Header />
<Grid container justify="center" alignItems="center" direction="column" >
<Grid item>
<NextLink href="/" passHref>
<MUILink underline="none" color="secondary" variant="h1">
Welcome To the Family Heirloom
</MUILink>
</NextLink>
</Grid>
</Grid>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</CacheProvider>
);
}
MyApp.propTypes = {
Component: PropTypes.elementType.isRequired,
emotionCache: PropTypes.object,
pageProps: PropTypes.object.isRequired,
};
Breakfast.js
import { React, useEffect, useState } from 'react'
import FilterMains from '../../src/ui/FilterMains'
import { useRouter } from 'next/dist/client/router'
import FoodCard from '../../src/ui/FoodCard'
import {
getFirestore, collection, query, where, onSnapshot
} from 'firebase/firestore'
const Breakfast = () => {
const router = useRouter();
const { id } = router.query;
const [breakfastloading, setBreakfastLoading] = useState(true);
const [breakfastRecipe, setBreakfastRecipe] = useState([]);
const db = getFirestore()
const docRef = collection(db, 'recipes')
//Query
const q = query(docRef, where("category", "==", 'Breakfast'))
useEffect(() => {
const getBreakfastFromFirebase = [];
onSnapshot(q, (snapshot) => {
snapshot.docs.forEach((doc) => {
getBreakfastFromFirebase.push({ ...doc.data() })
})
setBreakfastRecipe(getBreakfastFromFirebase)
setBreakfastLoading(false)
console.log(breakfastRecipe)
})
}, [breakfastloading, router.events]);
if (breakfastloading) {
return (
<h2>Loading Data</h2>
)
}
return (
<FoodCard recipes={breakfastRecipe} />
// <FoodCard recipes={recipes} />
)
}
export default Breakfast
FoodCard.js
import React from 'react'
import Card from '#mui/material/Card'
import CardHeader from '#mui/material/CardHeader';
import CardMedia from '#mui/material/CardMedia';
import Grid from '#mui/material/Grid'
import Container from '#mui/material/Container';
import Link from 'next/link'
import CardActionArea from '#mui/material/CardActionArea';
function FoodCard(props) {
return (
<div>
<Container>
< Grid container justify="center" alignItems="center" direction="row" >
<Grid container spacing={2}>
{props.recipes.map((recipe) => (
<Link href={`/recipes/${recipe.key}`} passHref>
<Grid key={recipe.id} item xs={12} md={6}>
<Card elevation={3} sx={{ maxWidth: 400 }}>
<CardActionArea>
<CardHeader
titleTypographyProps={{ fontWeight: "Bold" }}
title={recipe.title}
subheader={recipe.description}
/>
<CardMedia
component="img"
height="194"
image="/assets/comingsoon.jpg"
alt="Mac and Cheese"
/>
</CardActionArea>
</Card>
</Grid>
</Link>
))}
</Grid>
</Grid >
</Container>
</div>
)
}
export default FoodCard
Cracked it, although i can't fully explain why. In my learning i used two different methods for interfacing with firebase. The first method on the index page was what i would call the older way. The second method i used was in all of my other files, specific to interfacing with Version 9 of Firebase.
from this:
const subscriber = firebase.firestore()
.collection("recipes")
.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(querySnapshot)
getRecipesFromFirebase.push({
...doc.data(), //spread operator
key: doc.id, // `id` given to us by Firebase
});
});
to this:
let getRecipesFromFirebase = [];
getDocs(colRef)
.then((snapshot) => {
let getRecipesFromFirebase = [];
snapshot.docs.forEach((doc) => {
getRecipesFromFirebase.push({ ...doc.data(), key: doc.id })
})
setRecipes(getRecipesFromFirebase);
console.log(recipes);
setLoading(false);
})
.catch(error => {
console.log(error.message)
})
Would love somebody more knowledgable (3 months into React) than myself to tell me as to why though.
I currently have this practice code that uses Context and Memo Hooks API
codesandbox
Here is a code snippet
export const InputContext = createContext()
export const ParentProvider = ({ initialValues, children }) => {
console.log(initialValues)
const [values, setValues ] = useState(initialValues);
const value = {
values,
setValues
}
return <InputContext.Provider value={value}>{children}</InputContext.Provider>
}
What I want is to update the value of array that holds indicators using Context API after I click edit.
The problem is that I can't access the Context after accessing through the memo
You'll need to wrap all components that need access to the context with the provider. Something like this...
ParentProvider.js
import React, { createContext, useState } from "react";
const INITIAL_STATE = [];
export const InputContext = createContext(INITIAL_STATE);
export const ParentProvider = ({ children }) => {
const [values, setValues] = useState(INITIAL_STATE);
React.useEffect(() => {
console.log("[parentprovider.js]::new values", values);
}, [values]);
return (
<InputContext.Provider value={{ values, setValues }}>
{children}
</InputContext.Provider>
);
};
ShowIndicator.js
import React, { memo, useContext, useState } from "react";
import { Button } from "react-bootstrap";
import { InputContext } from "./ParentProvider";
const ShowIndicator = memo(
({ name, context }) => {
const [_name, _setName] = useState(name);
const [text, setText] = useState();
const { values, setValues } = useContext(InputContext);
const editData = e => {
let newValues = [...values];
newValues[values.indexOf(_name)] = text;
setValues(newValues);
_setName(text);
};
const handleTextChange = e => setText(e.target.value);
const renderDatas = () => {
return (
<div key={_name} className="d-flex justify-content-between">
<input
className="d-flex align-items-center"
defaultValue={_name}
onChange={handleTextChange}
/>
<div>
<Button
variant="info"
style={{ marginRight: "10px" }}
onClick={editData}
>
Edit
</Button>
<Button variant="dark">Delete</Button>
</div>
</div>
);
};
return <div style={{ marginBottom: "5px" }}>{renderDatas()}</div>;
},
(prev, next) => prev.value === next.value
);
export default ShowIndicator;
App.js
import React, { useState, useContext } from "react";
import "./styles.css";
import { Form, Button, Container } from "react-bootstrap";
import ShowIndicator from "./ShowIndicator";
import { InputContext } from "./ParentProvider";
function App() {
const [curText, setCurText] = useState("");
const { values, setValues } = useContext(InputContext);
const onSubmit = e => {
e.preventDefault();
if (!values.includes(curText)) {
values ? setValues([...values, curText]) : setValues([curText]);
setCurText("");
}
};
const onChange = e => setCurText(e.target.value);
return (
<div>
<Container style={{ marginTop: "10px", textAlign: "center" }}>
<div>Add Indicator</div>
<Form inline onSubmit={onSubmit} style={{ marginBottom: "1rem" }}>
<Form.Control
style={{ flex: "1 0 0" }}
onChange={onChange}
value={curText}
/>
<Button type="submit" variant="success">
Submit
</Button>
</Form>
{values &&
values.map((data, index) => {
return <ShowIndicator key={index} name={data} />;
})}
</Container>
</div>
);
}
export default App;
index.js
import React from "react";
import App from "./App";
import { render } from "react-dom";
import { ParentProvider } from "./ParentProvider";
render(
<ParentProvider>
<App />
</ParentProvider>,
document.getElementById("root")
);
Using useContext you need to pass the entire context object (not only Consumer). And just use it like this
const Component = () =>{
const context = useContext(InputContext)
const { values, setValues } = context
const handleChange = () => setValues('foo')
return(
<>
{values}
<button onClick={handleChange}>Change</button>
</>
)
}
I am trying to make a react component to display a notification on use.
The thing is that it's not working correctly...
The notification is supposed to appear when a user logs-in with an email with wrong provider
what am I doing is that I check for user email after he logs in if the mail doesn't contain the right provider I log him out.
what is happening is that the notification is showing up and disappearing quickly after logging the user out though I made a long interval for it.
I tried experimenting with material UI the react UI framework.
Nothing is working so far
Navbar.js
import React, { Component } from "react";
import withFirebaseAuth from "react-with-firebase-auth";
import * as firebase from "firebase/app";
import firebaseConfig from "../../firebase.config";
import "firebase/auth";
// Material UI
import { makeStyles } from "#material-ui/core/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import Button from "#material-ui/core/Button";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import AccountCircle from "#material-ui/icons/AccountCircle";
import Tooltip from "#material-ui/core/Tooltip";
import Notify from "../notification/notification"
const firebaseApp = firebase.initializeApp(firebaseConfig);
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1
},
menuButton: {
marginRight: theme.spacing(2)
},
title: {
flexGrow: 1
}
}));
const filler = () => {console.log(' ')}
function HomeNav(props) {
const { user, signOut, signInWithGoogle } = props;
const classes = useStyles();
const userMail = (user)?user.email.includes("#hijrah.org"):'';
let notify = false;
if(user && !userMail){
signOut();
notify = true;
}
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="menu"
>
<MenuIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>
News
</Typography>
{user? (
<Tooltip title={user.displayName}>
<IconButton
edge="end"
aria-label="account of current user"
aria-haspopup="true"
onClick={signOut}
color="inherit"
>
<AccountCircle />
</IconButton>
</Tooltip>
) : (
<Button color="inherit" onClick={signInWithGoogle}>
Login
</Button>
)}
</Toolbar>
</AppBar>
<React.Fragment>
{/*this is the part where i try to display notification on*/}
{(notify)? <Notify /> : <span></span>}
</React.Fragment>
</div>
);
}
class Nav extends Component {
render() {
return <HomeNav {...this.props} />;
}
}
const firebaseAppAuth = firebaseApp.auth();
const providers = {
googleProvider: new firebase.auth.GoogleAuthProvider()
};
providers.googleProvider.setCustomParameters({hd:"hijrah.org"});
export default withFirebaseAuth({
providers,
firebaseAppAuth
})(Nav);
my notification component notify.js
import React from "react";
import PropTypes from "prop-types";
import clsx from "clsx";
import CheckCircleIcon from "#material-ui/icons/CheckCircle";
import ErrorIcon from "#material-ui/icons/Error";
import InfoIcon from "#material-ui/icons/Info";
import CloseIcon from "#material-ui/icons/Close";
import { amber, green } from "#material-ui/core/colors";
import IconButton from "#material-ui/core/IconButton";
import Snackbar from "#material-ui/core/Snackbar";
import SnackbarContent from "#material-ui/core/SnackbarContent";
import WarningIcon from "#material-ui/icons/Warning";
import { makeStyles } from "#material-ui/core/styles";
const variantIcon = {
success: CheckCircleIcon,
warning: WarningIcon,
error: ErrorIcon,
info: InfoIcon
};
const useStyles1 = makeStyles(theme => ({
success: {
backgroundColor: green[600]
},
error: {
backgroundColor: theme.palette.error.dark
},
info: {
backgroundColor: theme.palette.primary.main
},
warning: {
backgroundColor: amber[700]
},
icon: {
fontSize: 20
},
iconVariant: {
opacity: 0.9,
marginRight: theme.spacing(1)
},
message: {
display: "flex",
alignItems: "center"
}
}));
function MySnackbarContentWrapper(props) {
const classes = useStyles1();
const { className, message, onClose, variant, ...other } = props;
const Icon = variantIcon[variant];
return (
<SnackbarContent
className={clsx(classes[variant], className)}
aria-describedby="client-snackbar"
message={
<span id="client-snackbar" className={classes.message}>
<Icon className={clsx(classes.icon, classes.iconVariant)} />
{message}
</span>
}
action={[
<IconButton
key="close"
aria-label="close"
color="inherit"
onClick={onClose}
>
<CloseIcon className={classes.icon} />
</IconButton>
]}
{...other}
/>
);
}
MySnackbarContentWrapper.propTypes = {
className: PropTypes.string,
message: PropTypes.string,
onClose: PropTypes.func,
variant: PropTypes.oneOf(["error", "info", "success", "warning"]).isRequired
};
export default function CustomizedSnackbars() {
const [open, setOpen] = React.useState(true);
const handleClose = (event, reason) => {
if (reason === "clickaway") {
return;
}
setOpen(false);
};
return (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
open={open}
autoHideDuration={60000}
onClose={handleClose}
>
<MySnackbarContentWrapper
onClose={handleClose}
variant="warning"
message="Please login with a #hijrah.org mail"
/>
</Snackbar>
</div>
);
}
I expect to display the error message after I log out the user and for it to stay for like 5 seconds.
I suspect what is happening in that when you log the user out, the Nav component is no longer rendered and therefore neither is the Notify Component as it is a child of Nav (Nav >> HomeNav >> Notify). Because it is no longer rendered the message disappears.
If that is the case, you should move the Notify to a higher level in your app so that it is displayed regardless of whether the user is logged in or not.
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();
}