I have a route /courses and when I do search for courses from the frontend(ReactJs) it takes me to this URL http://localhost:3001/courses/search?searchQuery=introducton but when I again click on courses button(that is on my navbar), it takes me to /courses but It doesn't show all the courses, it only shows the searched query that I got from the above query. Every time I need to refresh the page to get all the courses.
Courses.js file
import React, { useState } from "react"
import CourseHeader from "./CourseHeader"
import CoursesList from "./CoursesList"
import FilterCourse from "./FilterCourse"
import Filtertech from "./Filtertech"
import { useDispatch, useSelector } from "react-redux"
import { useHistory } from "react-router"
import { getCoursesBySearch } from "../../actions/courses"
const Courses = () => {
const dispatch = useDispatch()
const [search, setSearch] = useState("")
const history = useHistory()
const searchCourses = () => {
if (search.trim()) {
dispatch(getCoursesBySearch({ search }))
history.push(`/courses/search?searchQuery=${search || "none"}`)
} else {
history.push("/courses")
}
}
const handleKeyPress = (e) => {
if (e.keyCode === 13) {
searchCourses()
}
}
const courses = useSelector((state) => state.courses)
return (
<div className="row-start-2 row-end-3 col-start-1 col-end-2 mx-auto w-full max-w-5xl ">
<div className="grid grid-cols-layout-course gap-x-3">
<div>
<FilterCourse />
<Filtertech />
</div>
<div>
<CourseHeader
handleKeyPress={handleKeyPress}
setSearch={setSearch}
search={search}
/>
<CoursesList courses={courses} />
</div>
</div>
</div>
)
}
export default Courses
CourseList.js
import React from "react"
import CourseCard from "./CourseCard"
const CoursesList = ({ courses }) => {
return (
<div className="">
{courses && courses.map((course) => <CourseCard data={course} key={course._id} />)}
</div>
)
}
export default CoursesList
App.js
import "./App.css"
import "./assets/output.css"
import { BrowserRouter as Router, Switch, Route } from "react-router-dom"
import Navbar from "./components/layouts/Navbar"
import Courses from "./components/courses/Courses"
import { useEffect } from "react"
import { getCourses } from "./actions/courses"
function App() {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getCourses())
}, [dispatch])
return (
<Router>
<div className="App min-h-screen grid grid-rows-layout-desktop grid-cols-layout-desktop gap-10">
<Navbar />
<Switch>
<Route path="/courses">
<Courses />
</Route>
</Switch>
<Footer />
</div>
</Router>
)
}
export default App
Anyone please help me with this
Related
I am creating a React app which can be used to manage company events. There are two components related to events that aren't working at the moment. These are the edit event component, and the event card component which displays all the information relevant to the event. I'm trying to pass the event ID in order to identify the relevant event to be edited or viewed. The useParams() hook is used to pass the ID. When I set up a route for the edit event feature and view event card feature, I get the following errors in the console.
The error shown in the edit event route is,
No routes matched location "/editEvent/(event ID)".
The error shown in the event card route is,
No routes matched location "/event/(event ID)"
The (event ID) contains the ID of the event that I'm trying to edit or view. The correct ID was visible in the address of the route.
I have attached the code of all components related to the edit event and event card features.
src/App.js
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import AddProject from './Components/AddProject';
import AllProjects from './Components/AllProjects';
import AddEvents from './Components/AddEvents';
import AllEvents from './Components/AllEvents';
import EditProject from './Components/EditProject';
import ProjectDetails from './Components/ProjectDetails';
import EventCard from './Components/EventCard';
import EditEvent from './Components/EditEvent';
import { NoMatch } from './Components/NoMatch';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<AddProject />} />
<Route path="/allProjects" element={<AllProjects />} />
<Route path="/addEvents/:projectName" element={<AddEvents />} />
<Route path="/allEvents/:projectName" element={<AllEvents />} />
<Route path="/editProject/:id" element={<EditProject />} />
<Route Path="/project/:id" element={<ProjectDetails />} />
<Route Path="/event/:id" element={<EventCard />} />
<Route Path="/editEvent/:id" element={<EditEvent/>} />
<Route Path='*' element={<NoMatch/>}></Route>
</Routes>
</Router>
);
}
export default App;
src/Components/AllEvents.js
import { React, useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Button, Card } from 'react-bootstrap';
import axios from "axios";
function AllEvents() {
const [listOfEvents, setListOfEvents] = useState([]);
useEffect(() => {
axios.get("/getEvent").then((response) => {
setListOfEvents(response.data);
})
}, []);
const { projectName } = useParams();
const handleDelete = (_id) => {
axios.delete(`/deleteEvent/${_id}`)
.then((res) => {
console.log(res);
console.log(res.data);
})
const newList = listOfEvents.filter((event) => event._id !== _id);
alert("Event was deleted");
setListOfEvents(newList);
}
let navigate = useNavigate();
const filteredList = listOfEvents.filter((event) => event.projectName === projectName);
return (
<div id="allEvents">
<h1 style={{ textAlign: "center" }}>{projectName}</h1>
<h3>All Events</h3>
{filteredList.length > 0 ? (filteredList.map((events) => {
return (
<div>
<Card className="detailsCard" border="dark">
<div className="details">
<span className="title">Event Name:</span>
<span className="data">{events.eventName}</span>
</div>
<div className="details">
<span className="title">Event Description:</span>
<span className="data">{events.eventDescription}</span>
</div>
<div className="details">
<span className="title">Event Start Date:</span>
<span className="data">{events.eventStartDate}</span>
</div>
<div className="details">
<span className="title">Event End Date:</span>
<span className="data">{events.eventEndDate}</span>
</div>
<div>
<Button variant="success" size="sm" type="submit" onClick={() => { navigate(`/event/${events._id}`) }}>View Event Card</Button>
<Button className="eventButton" variant="warning" size="sm" type="submit" onClick={() => { navigate(`/editEvent/${events._id}`) }}>Edit Event</Button>
<Button className="eventButton" variant="danger" size="sm" type="submit" onClick={() => { handleDelete(events._id) }}>Delete Event</Button>
</div>
</Card>
</div>
);
})
) : (
<div>
<p>No events have been added yet</p>
</div>
)}
<br />
<Button variant="secondary" size="lg" onClick={() => { navigate(`/addEvents/${projectName}`) }}>Add new event</Button>
</div>
);
}
export default AllEvents;
src/Components/EditEvent.js
import { React, useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { Form, Card, Button,CloseButton } from 'react-bootstrap';
import axios from "axios";
function EditEvent() {
const [projectName, setProjectName] = useState();
const [eventName, setEventName] = useState();
const [eventDescription, setEventDescription] = useState();
const [eventStartDate, setEventStartDate] = useState();
const [eventEndDate, setEventEndDate] = useState();
const { id } = useParams();
let navigate = useNavigate();
const editEvent = () => {
axios.put(`/updateEvent/${id}`, {
eventName,
eventDescription,
eventStartDate,
eventEndDate
}).then((res) => {
alert("Project has been edited");
console.log("Project edited");
});
}
useEffect(() => {
axios.get(`/getEvent/${id}`).then((res) => {
setProjectName(res.data.events.projectName);
setEventName(res.data.events.eventName);
setEventDescription(res.data.events.eventDescription);
setEventStartDate(res.data.events.eventStartDate);
setEventEndDate(res.data.events.setEventEndDate);
console.log(res.data.events);
});
// eslint-disable-next-line
}, []);
return (
<div>
<div className="eventCard">
<Card border="dark" >
<Card.Header>
<div className="eventCardHeader">
Edit an Event
<CloseButton className="closeButton" onClick={() => { navigate(`/allEvents/${projectName}`) }} />
</div>
</Card.Header>
<Card.Body>
<Form>
<Form.Group>
<h5>Edit Event Name</h5>
<Form.Control as="textarea"
rows={1}
placeholder='Add Event Name'
value={eventName}
onChange={(event) => { setEventName(event.target.value) }}>
</Form.Control><br />
</Form.Group>
<Form.Group>
<h5>Edit Event Description</h5>
<Form.Control as="textarea"
rows={3}
placeholder='Add Event Description'
value={eventDescription}
onChange={(event) => { setEventDescription(event.target.value) }}>
</Form.Control><br />
</Form.Group>
<h5>Edit Event Duration</h5>
<Form.Label>Start Date</Form.Label><br />
<input
type="date"
min={new Date().toISOString().split('T')[0]}
max="2030-12-31"
value={eventStartDate}
onChange={(event) => { setEventStartDate(event.target.value) }} /><br />
<Form.Label>End Date</Form.Label><br />
<input
type="date"
min={new Date().toISOString().split('T')[0]}
max="2030-12-31"
value={eventEndDate}
onChange={(event) => { setEventEndDate(event.target.value) }} /><br />
</Form>
<Card.Footer style={{ paddingLeft: '50%' }}>
<Button variant="warning" size="lg" type="submit" onClick={editEvent}>Edit Project</Button>
</Card.Footer>
</Card.Body>
</Card>
</div>
</div>
);
}
export default EditEvent;
src/Components/EventCard.js
import { React, useEffect, useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import axios from "axios";
import { CloseButton } from 'react-bootstrap';
function EventCard(){
const [eventName,setEventName] = useState();
const [eventDescription,setEventDescription] = useState();
const [eventStartDate,setEventStartDate] = useState();
const [eventEndDate,setEventEndDate] = useState();
const { id } = useParams();
let navigate = useNavigate();
useEffect(() =>{
axios.get(`/getEvent/${id}`).then((res) => {
console.log("Hello");
setEventName(res.data.event.eventName);
setEventDescription(res.data.event.eventDescription);
setEventStartDate(res.data.event.eventStartDate);
setEventEndDate(res.data.event.eventEndDate);
console.log("EventData",res.data.event);
});
// eslint-disable-next-line
},[]);
return(
<div>
<CloseButton className="closeButton" onClick={() => { navigate("/allProjects") }} />
{eventName}
{eventDescription}
{eventStartDate}
{eventEndDate}
</div>
);
}
export default EventCard;
routes/events.js
const express = require('express');
const router = express.Router();
const eventModel = require('../models/Events');
// Add an event
router.post("/createEvent", async (req, res) => {
const event = req.body;
const newEvent = new eventModel(event);
await newEvent.save();
res.json(event);
});
// Retrieve all events
router.get("/getEvent", (req, res) => {
eventModel.find({}, (err, result) => {
if (err) {
res.json(err);
} else {
res.json(result);
}
});
});
// Retrieve a specific event
router.get("/getEvent/:id", (req,res) => {
let eventId = req.params.id;
eventModel.findById(eventId,(err,event) => {
if(err){
return res.status(400).json({success:false, err});
}
return res.status(200).json({
success:true,
event
});
});
});
// Update an event
router.put('/updateEvent/:id',(req,res) => {
eventModel.findByIdAndUpdate(
req.params.id,
{
$set:req.body
},
(err,updatedEvent) => {
if(err){
return res.status(400).json({error:err});
}
return res.status(200).json({
success:"Updated succesfully"
});
}
);
});
// Delete an event
router.delete('/deleteEvent/:id',(req,res) => {
eventModel.findByIdAndRemove(req.params.id).exec((err,deletedEvent) => {
if(err) return res.status(400).json({
message:"Delete unsuccessful",err
});
return res.json({
message:"Delete successful",deletedEvent
});
});
});
module.exports = router;
Server.js
const express = require('express');
const app = express();
const mongoose = require('mongoose');
const cors = require('cors');
const projectRoutes = require('./routes/projects');
const eventRoutes = require('./routes/events');
app.use(express.json());
app.use(cors());
app.use(projectRoutes);
app.use(eventRoutes);
app.get("/", (req, res) => {
res.send("Hello world");
}
);
//Actual url was removed when posting to stackoverflow
const DB_URL = '';
mongoose
.connect(DB_URL)
.then(() => {
console.log('MongoDB connected');
})
.catch((err) => console.log('DB connection error',err));
app.listen(5000, () => console.log("Server is running in port 5000"));
I appreciate any help that I can get.
I tried to create an authentication website with Firebase using email and password. I can't even load the Login page.
Here's Auth.js
import React, { useState, useEffect} from "react";
import { auth } from './config'
import { onAuthStateChanged } from "firebase/auth";
export const AuthContext = React.createContext();
export const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
useEffect(() => {
onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
})
}, [])
return (
<AuthContext.Provider value={{currentUser}}>
{children}
</AuthContext.Provider>
)
}
And this is Login.js
import React, {useContext ,useState } from "react";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import "./Login.css";
import { auth } from './config'
import { signInWithEmailAndPassword } from "firebase/auth";
import { AuthContext } from "./Auth";
import { useHistory } from "react-router-dom";
const Login = () => {
let history = useHistory();
const handleSubmit = (event) => {
event.preventDefault();
const { email, password } = event.target.elements;
signInWithEmailAndPassword(auth, email.value, password.value)
.then((userCredential) => {
const user = userCredential.user;
console.log(user.uid);
})
.catch((error) => {
console.log(error.massage);
});
}
const currentUser = useContext(AuthContext);
if(currentUser) {
return history.push('/dashboard');
}
return (
<div className="Login">
<h1>Login</h1>
<Form onSubmit={handleSubmit}>
//Login Form
</Form>
</div>
);
}
export default Login
And DashBoard.js
import React, {useContext} from 'react'
import { AuthContext } from './Auth'
import { auth } from './config'
import { signOut } from 'firebase/auth'
import { useHistory } from "react-router-dom";
const DashBoard = () => {
const currentUser = useContext(AuthContext);
let history = useHistory();
if(!currentUser) {
return history.push('/login');
}
const signOutFunc = () => {
signOut(auth)
}
return (
<div>
<div className='container mt-5'>
<h1>Welcome</h1>
<h2>If you see this you are logged in.</h2>
<button className='btn btn-danger' onClick={signOutFunc}>Sign Out</button>
</div>
</div>
)
}
export default DashBoard;
Lastly App.js
import { BrowserRouter as Router, Route, Switch} from 'react-router-dom'
import Login from './Login'
import DashBoard from './DashBoard';
import { AuthProvider } from './Auth'
function App() {
return (
<AuthProvider>
<Router>
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/dashboard" component={Dashboard} />
</Switch>
</Router>
</AuthProvider>
);
}
export default App;
When I open /login, it would send me to /dasgboard immediately. If I typed /login again it gives me this error
Error: Login(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
I can't figure it out what's wrong with it. Please help me.
Thank you
You have multiple places in your code where you return history.push('/dashboard'); or another path. You should return there a null:
if(!currentUser) {
history.push('/login');
return null
}
I am try to call dispatch method from useEffect hook in my React app but after rendering the useEffect continue looping.
here is my code....
category.actions.js
here is the actions perform
import axios from "../../apiFetch/axios";
import { categoryActions } from './constants';
export const getCategories = () => {
return async (dispatch) => {
dispatch({ type: categoryActions.GET_CATEGORY_ACTION})
const res = await axios.get('/category/getCategories');
console.log(res)
if(res.status === 200){
const {categoryList} = res.data;
dispatch({
type: categoryActions.GET_CATEGORY_SUCCESS,
payload: {
categories: categoryList
}
});
}else{
dispatch({
type: categoryActions.GET_CATEGORY_FAILURE,
payload: {
error: res.data.error
}
});
}
}
}
category.reducer.js
this is reducer code
import { categoryActions } from "../actions/constants";
const initialState = {
laoding: false,
categoryList: [],
error: null
}
const categoryReducer = (state = initialState, action) => {
switch(action.type){
case categoryActions.GET_CATEGORY_ACTION:
return{
loading: true
};
case categoryActions.GET_CATEGORY_SUCCESS:
return{
loading: false,
categoryList: action.payload.categories
};
case categoryActions.GET_CATEGORY_FAILURE:
return{
loading: false,
error: action.payload.error
}
default:
return{
...initialState
}
}
}
export default categoryReducer;
Category components where I used useEffect hooks
import Layout from '../../components/Layout';
import {Container, Row, Col} from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux';
import { getCategories } from '../../redux/actions';
const Category = () => {
const dispatch = useDispatch();
const state = useSelector(state => state.categoryReducer)
useEffect(() => {
dispatch(getCategories());
},[]);
return (
<Layout sidebar>
<Container>
<Row>
<Col>
<div style={{display: 'flex', justifyContent: 'space-between'}}>
<h3>Category</h3>
<button>Add</button>
</div>
</Col>
</Row>
</Container>
</Layout>
)
}
export default Category;
constant.js
here is the constant i used as type
export const categoryActions = {
GET_CATEGORY_ACTION: 'GET_CATEGORY_ACTION',
GET_CATEGORY_SUCCESS: 'GET_CATEGORY_SUCCESS',
GET_CATEGORY_FAILURE: 'GET_CATEGORY_FAILURE'
}
here is index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {Provider} from 'react-redux';
import store from './redux/store';
import { BrowserRouter as Router } from 'react-router-dom';
window.store = store;
ReactDOM.render(
<Provider store={store}>
<Router>
<React.StrictMode>
<App />
</React.StrictMode>
</Router>
</Provider>,
document.getElementById('root')
);
reportWebVitals();
here is App.js parent component
import Signin from './containers/Signin';
import Signup from './containers/Signup';
import PrivateRoute from './components/HOC/PrivateRoute';
import {useDispatch, useSelector} from 'react-redux';
import { isUserLogin } from './redux/actions';
import Products from './containers/Products';
import Orders from './containers/Orders';
import Category from './containers/Category';
function App() {
const auth = useSelector(state => state.authReducer);
const dispatch = useDispatch();
useEffect(() => {
if(!auth.authenticate){
dispatch(isUserLogin());
}
}, [])
return (
<div className="App">
<Switch>
<PrivateRoute path="/" exact component={Home} />
<PrivateRoute path="/products" component={Products} />
<PrivateRoute path="/orders" component={Orders} />
<PrivateRoute path="/category" component={Category} />
<Route path="/signup" component={Signup} />
<Route path="/signin" component={Signin} />
</Switch>
</div>
);
}
export default App;
I'm looking for a way to be able to open the drawer, with the least possible re-rendering of the DOM.
Every time the drawer opens, a re-rendering of the route component occurs, however I think it is unnecessary since the child components have not changed and are only re-rendered due to the fact that I am updating the state to open the drawer.
Aprecio su ayuda y orientacion con este tema
This is my code for Appbar.js
import React, { useState, Fragment, lazy, Suspense, useCallback} from "react";
import { Switch,Router, Route, Link, useLocation } from "react-router-dom";
import { createBrowserHistory } from "history";
import { withStyles } from "#material-ui/core/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import logo_LH_webp from '../../imagenes/logo.webp';
import logo_LH_png from '../../imagenes/logo.png';
import AccountCircle from '#material-ui/icons/AccountCircle';
import { useSelector } from 'react-redux'
import { isEmpty } from 'react-redux-firebase'
import { useFirebase } from "react-redux-firebase";
import { store } from '../../index'
import { setMesa } from "../../slices/cart";
import axios from 'axios';
import {MemoMyDrawer} from './MyDrawer'
import Button from '#material-ui/core/Button';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
import PopupState, { bindTrigger, bindMenu } from 'material-ui-popup-state';
const drawerWidth = 240;
const history = createBrowserHistory();
const styles = theme => ({
root: {
flexGrow: 1
},
flex: {
flex: 1
},
drawerPaper: {
position: "relative",
width: drawerWidth
},
menuButton: {
marginLeft: "auto",
},
toolbarMargin: theme.mixins.toolbar,
aboveDrawer: {
zIndex: theme.zIndex.drawer + 1
},
appBar: {
backgroundColor: "white"
}
});
let vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
const menuId = 'primary-search-account-menu';
const getQuery = () => {
if (typeof window !== "undefined") {
return new URLSearchParams(window.location.search);
}
return new URLSearchParams();
};
function AppBarInteraction({ classes, variant }) {
const query = getQuery();
console.log("MESA")
console.log()
const cart = useSelector(state => state.cart);
let valor = getQuery().get('q');
if (cart.mesa === "")
{
store.dispatch(setMesa(valor));
}
else
{
valor = cart.mesa;
}
const auth = useSelector(state => state.firebase.auth);
console.log('Auth vacio')
console.log(isEmpty(auth))
console.log(auth.isEmpty);
const [drawer, setDrawer] = useState(false);
const firebase = useFirebase();
firebase.auth().onAuthStateChanged(async (user) => {
if (user) {
console.log("usuario");
console.log(user);
//setAutorizado(true);
const token = await user.getIdToken();
axios.defaults.headers.common['Authorization'] = "Bearer " + token;
} else {
console.log("No logueado");
//setAutorizado(false);
}
});
// const [autorizado, setAutorizado] = React.useState(null);
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
console.log('Autorizado?');
//console.log(autorizado);
const toggleDrawer = useCallback(() => {
console.log(drawer)
setDrawer(!drawer);
}, [drawer]);
const logout = () => {
//setAutorizado(false);
firebase.logout();
}
const perfil = () => {
history.push("/perfil");
}
const MemoMyDraw = (drawer) => (
<MemoMyDrawer
open={drawer}
variant="temporary"
onClose={toggleDrawer}
toggleDrawer={toggleDrawer}
/>
)
const MenuPropio = () => (
// <Menu
// id="menu-appbar"
// anchorEl={anchorEl}
// open={open}
// onClose={handleClose}
// >
// <MenuItem onClick={perfil}>Perfil de Usuario</MenuItem>
// <MenuItem onClick={logout}>Cerrar Sesión</MenuItem>
// </Menu>
<PopupState variant="popover" popupId="demo-popup-menu">
{(popupState) => (
<React.Fragment>
<IconButton
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
color="primary"
{...bindTrigger(popupState)}
>
<AccountCircle />
</IconButton>
<Menu {...bindMenu(popupState)}>
<MenuItem onClick={perfil}>Perfil de Usuario</MenuItem>
<MenuItem onClick={logout}>Cerrar Sesión</MenuItem>
</Menu>
</React.Fragment>
)}
</PopupState>
)
return (
<div className={classes.root}>
<Fragment>
<Router history={history}>
<AppBar position="fixed" className={classes.appBar}>
<Toolbar>
<Link to={"/" + "?q=" + valor} ><picture><source srcSet={logo_LH_webp} type="image/webp" /><img alt="logo" srcSet={logo_LH_png} height="56" width="77" /></picture></Link>
<IconButton
color="default"
aria-label="open drawer"
edge="end"
onClick={toggleDrawer} //{handleDrawerToggle}
className={classes.menuButton}
>
<MenuIcon />
</IconButton>
{true && (
<Fragment>
<div>
<MenuPropio />
</div>
</Fragment>
)}
</Toolbar>
</AppBar>
</Router>
<div className={classes.toolbarMargin} />
</Fragment>
{MemoMyDraw(drawer)}
</div>
);
}
export default React.memo(withStyles(styles)(AppBarInteraction));
And this is the code for MyDrawer.js
import React, { Suspense, lazy} from "react";
import { withStyles } from "#material-ui/core/styles";
import Drawer from "#material-ui/core/Drawer";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import PrivateRoute from "../PrivateRoute/PrivateRoute"
import clsx from "clsx";
import Divider from "#material-ui/core/Divider";
import { Switch,Router, Route, Link, useLocation } from "react-router-dom";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import InboxIcon from "#material-ui/icons/MoveToInbox";
import MailIcon from "#material-ui/icons/Mail";
import { createBrowserHistory } from "history";
import { useSelector } from 'react-redux'
import { isLoaded, isEmpty } from 'react-redux-firebase'
import Prueba from '../Prueba/Prueba';
const Cart = lazy(() => import ('../Cart/Cart'));
const Feedback = lazy(() => import ('../Feedback/Feedback'));
const Perfil = lazy(() => import ('../Perfil/Perfil'));
const OneClick = lazy(() => import ('../OneClick/OneClick'));
const Login = lazy(() => import ('../Login/Login'));
const Menu_LH = lazy(() => import ('../Menu/Menu'));
const Principal = lazy(() => import('../Principal/Principal'));
const ProductList = lazy(() => import( '../ProductList/ProductList'));
const Carta= lazy(() => import( '../Carta/Carta'));
const ProductosCategoria = lazy(() => import('../ProductosCategoria/ProductosCategoria'));
const InfoProducto = lazy(() => import('../InfoProducto/InfoProducto'));
const Confirmacion = lazy(() => import('../Confirmacion/Confirmacion'));
const drawerWidth = 240;
const history = createBrowserHistory();
function AuthIsLoaded({ children }) {
const auth = useSelector(state => state.firebase.auth)
if (!isLoaded(auth)) return <div></div>;
return children
}
console.log("DRAWER----------------------------")
const getQuery = () => {
if (typeof window !== "undefined") {
return new URLSearchParams(window.location.search);
}
return new URLSearchParams();
};
function switchResult(a){
switch(a){
case 'Tu Pedido Actual':
return 'pedido';
break;
case 'Carta':
return 'carta';
break;
case 'Inicio':
return '';
break;
case 'Carta':
return 'Carta';
break;
case 'Prueba':
return 'Prueba';
break;
case 'Comentarios':
return 'feedback';
break;
default:
return "OK";
}
}
const styles = theme => ({
root: {
flexGrow: 1
},
flex: {
flex: 1
},
drawerPaper: {
position: "relative",
width: drawerWidth
},
menuButton: {
marginLeft: "auto",
},
toolbarMargin: theme.mixins.toolbar,
aboveDrawer: {
zIndex: theme.zIndex.drawer + 1
},
appBar: {
backgroundColor: "white"
}
});
const MyDrawer = withStyles(styles)(
({ classes, variant, open, onClose, toggleDrawer }) => (
<Router history={history}>
<Drawer
variant="temporary"
open={open}
onClose={onClose}
classes={{
paper: classes.drawerPaper
}}
>
<div
className={clsx({
[classes.toolbarMargin]: variant === "persistent"
})}
/>
<Divider />
<List>
{["Inicio", "Carta" , "Tu Pedido Actual", "Comentarios" ].map((text, index) => (
<ListItem key={text} onClick={toggleDrawer} component={Link} to={"/" + switchResult(text) + "?q=" + getQuery().get('q')}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
<main className={classes.content}>
<Suspense fallback={<div>Cargando...</div>}>
<Switch>
<AuthIsLoaded>
<PrivateRoute exact path = "/" component={Principal}></PrivateRoute>
<PrivateRoute exact path="/perfil" component={Perfil}></PrivateRoute>
<PrivateRoute exact path="/OneClick"component={OneClick}></PrivateRoute>
{/* <Route exact path="/Menu" render={() => <div id="G"><ProductList /></div>} /> */}
<PrivateRoute path="/carta"component={Carta}></PrivateRoute>
<PrivateRoute path="/pedido"component={Cart}></PrivateRoute>
{/* <Route path="/carta" render={() => <div id="G"><ProductList /></div>} /> */}
{/* <PrivateRoute exact path="/"><div id="G"><Principal /></div></PrivateRoute> */}
<Route exact path="/Categoria/:categoriaId"><div id="ProductosCategoria"><ProductosCategoria /></div></Route>
<Route exact path="/login"><div id="Login"><Login /></div></Route>
<Route exact path="/feedback" > <div id="Feedback"><Feedback /></div></Route>
</AuthIsLoaded>
</Switch>
</Suspense>
</main>
</Router>
)
);
export const MemoMyDrawer = React.memo(withStyles(styles)(MyDrawer));
You have to separate the reference to drawer's state open from MyDrawer because when open chage re-render MyDrawer. My solution was to extends Drawer and manage its state with redux using this code:
import { useSelector, useDispatch } from "react-redux";
function VDrawer(props){
const dispatch = useDispatch();
const onClose=() => {dispatch({ type: "drawer"})};
const drawer = useSelector((state) => state.drawer);
return <Drawer variant="temporary"
open={drawer}
onClose={onClose}
ModalProps={{
keepMounted: true,
}}>
{props.children}
</Drawer>
}
In this way the state drawer wich open/close drawer don't affect to MyDrawer
I have created a e Commerce App in react. As you can see from the screenshot below, that when I click on the Apparels->Girls->Shoes , the data is not displayed in the screen.
So first, in the index.js file, I have set the BrowserRouter and created a component Main which holds all my other components.
index.js
import React from "react";
import ReactDOM from "react-dom";
import Main from "./Main";
import "./index.css";
import 'bootstrap/dist/css/bootstrap.css';
import {Route, NavLink, BrowserRouter} from 'react-router-dom';
ReactDOM.render((
<BrowserRouter>
<Main/>
</BrowserRouter>
)
,
document.getElementById("root")
);
After this I have created Main.js, where I have created components for Navigation and PLPMenu( which should display after clicking on the Girls->Shoes). Also in the Main.js, I have set the switch and Route paths
Main.js
import React, { Component } from "react";
import 'bootstrap/dist/css/bootstrap.min.css';
import { Route, Switch } from 'react-router-dom';
import Navigation from "./components/topNavigation";
import Footer from "./components/Footer";
import Banner from "./components/Banner";
import PLPMenu from "./components/PLPMenu";
import PDP from "./components/PDP";
import Home from "./components/Home";
class Main extends Component {
render() {
return (
<div>
<Navigation />
<Switch>
<Route exact path="/" component={Home} />
<Route path="Apparel/Girls/:id" component={PLPMenu}/>
<Route path="/PDP" component={PDP} />
<Route path="/Banner" component={Banner} />
<Route path="/Footer" component={Footer} />
</Switch>
</div>
)
}
}
export default Main;
In the topNavigation.js, I'm displaying the first level of categories like Apparel, Electronics, Grocery etc. Also, I have created, a component SubMenu for displaying the second level of categories like Girls, Boys, Women etc.
topNavigation.js
import React, { Component } from 'react';
import axios from 'axios';
import SubMenu from './subMenu';
class Navigation extends Component {
state = {
mainCategory: []
}
componentDidMount() {
axios.get('http://localhost:3030/topCategory')
.then(res => {
console.log(res.data.express);
this.setState({
mainCategory: res.data.express.catalogGroupView
})
})
}
render() {
const { mainCategory } = this.state;
return mainCategory.map(navList => {
return (
<ul className="header">
<li key={navList.uniqueID}>
<a className="dropbtn ">{navList.name} </a>
<ul className="dropdown-content">
<SubMenu below={navList.catalogGroupView} />
</ul>
</li>
</ul>
)
})
}
}
export default Navigation;
subMenu.js
In the submenu.js, I have created one more component SubListMenu for displaying the inner categories like Shoes, Pants, Skirts, Tops etc.
import React, { Component } from 'react';
import SubListMenu from './subListMenu';
class SubMenu extends Component {
render() {
const { below } = this.props;
return below.map(sub => {
return (
<React.Fragment>
<li key={sub.uniqueID}>
<a>{sub.name}</a>
{
<ul className="sub-menu">
{sub.catalogGroupView !== undefined && <SubListMenu id={sub.uniqueID} subBelow={sub.catalogGroupView} />}
</ul>
}
</li>
</React.Fragment>
)
})
}
}
export default SubMenu;
subListMenu.js
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
class SubListMenu extends Component {
render() {
const { subBelow, id } = this.props;
console.log(subBelow)
return(
<React.Fragment>
{subBelow && subBelow.map(subl => {
return (
<li key={subl.uniqueID}><Link to = {`Apparel/Girls/${subl.name}/${ subl.uniqueID }`}>{subl.name}</Link></li>
)
})
}
</React.Fragment>
)
}
}
export default SubListMenu;
As you can see from my subListMenu.js code, that I have set the Link to PLPMenu.js. But in my case it's not happening. Also, the part Apparel/Girls in the Link, I have hard coded which i'm not able to make it dynamic.
PLPMenu.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
class PLPMenu extends Component {
state = {
shoeCategory: []
}
componentDidMount() {
let pathname= this.props.match.params.id
console.log(pathname)
axios.get(`http://localhost:3030/${pathname}`)
.then(res => (res.json()))
.then(data => {
this.setState({
shoeCategory: data.express.catalogEntryView
})
});
}
render() {
const { shoeCategory } = this.state;
const picUrl = 'https://149.129.128.3:8443'
return (
<div>
<div className="container">
<div className="row">
{
shoeCategory && shoeCategory.map(shoeList => (
<div className="col-md-4">
<h2 key={shoeList.uniqueID}></h2>
<img src={picUrl + shoeList.thumbnail} />
<Link to="/PDP"><p className="pdp">{shoeList.name}</p></Link>
<p>Price : {shoeList.price[0].value} {shoeList.price[0].currency}</p>
</div>
))
}
</div>
</div>
</div>
)
}
}
export default PLPMenu;
For fetching the data, I have used a node server.
server.js
const express = require('express');
const cors = require('cors');
const Client = require('node-rest-client').Client;//import it here
const app = express();
app.use(cors());
app.get('/topCategory', (req, res) => {
var client = new Client();
// direct way
client.get("http://149.129.128.3:3737/search/resources/store/1/categoryview/#top?depthAndLimit=-1,-1,-1,-1", (data, response) => {
res.send({ express: data });
});
});
app.get('/GirlShoeCategory', (req, res) => {
var client = new Client();
// direct way
client.get("http://149.129.128.3:3737/search/resources/store/1/productview/byCategory/10015", (data, response) => {
res.send({ express: data });
});
});
const port = 3030;
app.listen(port, () => console.log(`Server running on port${port}`));
I don't know where my code is getting wrong. Maybe I feel that from the node server, there is a mismatch with the reactjs routes, for which only in the url, it's displaying the link but not the contents. Can someone please give me an insight on this. My console browser window:
for this issue
In the PLPMenu.js page, I'm trying to fetch the data. But all I'm getting is this undefined.
componentDidMount() {
let pathname= this.props.match.params.id
console.log(this.props.match.params.id)
axios.get(`http://localhost:3030/${pathname}`)
.then(res => {return res.json();})
.then(data => {
this.setState({
shoeCategory: data.express.catalogEntryView
})
});
}
try this it will solve undefined issue.
I believe you have to take id from subBelow instead this.props.id.
so change the code like this.
<li key={subl.uniqueID}><Link to = {`Apparel/Girls/${ subl.uniqueID }`}>{subl.name}</Link></li>
The reason you get undefined in URL bar because you are not passing the unique id from SubMenu down to SubListMenu component.
What you need to do is
SubMenu.js
import React, { Component } from 'react';
import SubListMenu from './subListMenu';
class SubMenu extends Component {
render() {
const { below } = this.props;
return below.map(sub => {
return (
<React.Fragment>
<li key={sub.uniqueID}>
<a>{sub.name}</a>
{
<ul className="sub-menu">
{sub.catalogGroupView !== undefined && <SubListMenu id={sub.uniqueID} subBelow={sub.catalogGroupView} />}
</ul>
}
</li>
</React.Fragment>
)
})
}
}
export default SubMenu;
SubListMenus.js
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
class SubListMenu extends Component {
render() {
const { subBelow, id } = this.props;
console.log(subBelow)
return(
<React.Fragment>
{subBelow && subBelow.map(subl => {
return (
<li key={subl.uniqueID}><Link to = {`Apparel/Girls/${ id }`}>{subl.name}</Link></li>
)
})
}
</React.Fragment>
)
}
}
export default SubListMenu;
Regarding below issue You need to do res.json() and in next .then get the data
In the PLPMenu.js page, I'm trying to fetch the data. But all I'm getting is this undefined.
Do this
componentDidMount() {
let pathname= this.props.match.params.id
console.log(this.props.match.params.id)
axios.get(`http://localhost:3030/${pathname}`)
.then(res => (res.json()))
.then(data => {
this.setState({
shoeCategory: data.express.catalogEntryView
})
});
}
Edit:
Add below condition in PLPMenu component
{shoeCategory && shoeCategory.map(shoeList => (