I am trying to follow this SO Answer to "go home" after I close my Modal...
import React, { Suspense, useState } from 'react';
import { BrowserRouter, Route, Switch, useHistory } from "react-router-dom";
import './App.css';
import { Button } from 'reactstrap';
import Slideshow from './components/Slideshow.js';
import LogoHeader from './components/LogoHeader';
import Login from "./components/Login/Login.js";
import ChangePassword from "./components/Login/ChangePassword.js";
import useToken from "./components/useToken";
import NewFooter from "./components/NewFooter";
import GetCategories from "./components/GetCategories";
import { formatter } from './components/common.js'
import { GetCart, Increment, Decrement } from './components/ShoppingCart/CartHandler.js'
import CheckoutModal from './components/ShoppingCart/CheckoutModal.js'
const FeaturedCards = React.lazy(() => import('./components/featuredCards'));
const ProdCard = React.lazy(() => import('./components/productCards'));
const Portal = React.lazy(() => import('./components/Portal/portal'));
function App() {
const { token, setToken } = useToken();
const [cart, setCart] = useState(GetCart());
const [isOpen, setIsOpen] = useState(false);
const history = useHistory();
if (!token) {
return <Login setToken={setToken} />
}
if (token.forcePasswordChange === true) {
return <ChangePassword setToken={setToken} />
}
function handleIncrementClick(item) {
Increment(item.ItemNumber, cart);
setCart(GetCart);
}
function handleDecrementClick(item) {
Decrement(item.ItemNumber, cart);
setCart(GetCart);
}
let onRequestClose = () => {
setIsOpen(false);
setCart(GetCart);
history.push('/')
}
let handleClick = () => {
setIsOpen(true)
}
return (
<div className="App">
<BrowserRouter>
<p className="greeting">Hello, {token.firstName}</p>
<LogoHeader />
<GetCategories />
<Switch>
<Route path="/cart">
<Suspense fallback={<div>Loading...</div>}>
<div>
<table className="table table-striped table-bordered">
<thead>
<tr>
<th className="number">Item Number</th>
<th className="price">Price</th>
<th className="quantity">Quantity</th>
</tr>
</thead>
<tbody>
{cart.map(item =>
<tr key={item.ItemNumber}>
<td>{item.ItemNumber}</td>
<td>{formatter.format(item.Price)}</td>
<td>{item.Quantity}</td>
<td><Button className="BtnIncrement" onClick={() => handleIncrementClick(item)}>{'+'}</Button></td>
<td><Button className="BtnDecrement" onClick={() => handleDecrementClick(item)}>{'-'}</Button></td>
</tr>
)}
</tbody>
</table>
<Button onClick={() => handleClick()}>Checkout</Button>
{isOpen ? <CheckoutModal onRequestClose={onRequestClose} /> : null}
</div>
</Suspense>
</Route>
<Route path="/portal">
<Suspense fallback={<div>Loading...</div>}>
<Portal />
</Suspense>
</Route>
<Route path="/">
<Slideshow id="slideshow" />
<div id="productContainer">
<br />
<h3>Featured Products</h3>
<br />
<Suspense fallback={<div>Loading...</div>}>
<FeaturedCards setCartFunc={setCart} />
</Suspense>
<br />
<h3>Most Popular</h3>
<br />
<Suspense fallback={<div>Loading...</div>}>
<ProdCard />
</Suspense>
<br />
<h3>New Products</h3>
<br />
<Suspense fallback={<div>Loading...</div>}>
<ProdCard />
</Suspense>
</div>
</Route>
</Switch>
<NewFooter />
</BrowserRouter>
</div>
);
}
export default App;
UPDATE
As requested, my CheckoutModal:
import React from 'react';
import "./Checkout.scss"
import AddressBlock from './AddressBlock';
import PaymentBlock from './PaymentBlock';
import ShippingBlock from './ShippingBlock';
import TotalsBlock from './TotalsBlock';
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
class CheckoutModal extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'React',
shippingMethod: null
};
}
setShippingMethod(method) {
this.setState(state => ({ ...state, shippingMethod: method }));
}
render() {
const buttonStyles = {
position: "absolute",
top: "-35px",
right: "10px",
border: "0",
background: "none"
};
return (
<Modal id="checkout"
isOpen
size='xl'>
<button onClick={this.props.onRequestClose} style={buttonStyles}>x</button>
<ModalHeader className="header">
Checkout
</ModalHeader>
<ModalBody>
<div className="row">
<AddressBlock />
<ShippingBlock setShippingMethod={this.setShippingMethod.bind(this)} shippingMethod={this.state.shippingMethod}/>
</div>
<div className="row">
<PaymentBlock onRequestClose={this.props.onRequestClose} />
<TotalsBlock />
</div>
</ModalBody>
</Modal>
);
}
}
export default CheckoutModal;
I am getting history is undefined from my function onRequestClose. I tried prepending this but that didn't work. Is this because App.js is not a class component or rather just a function? Any tips or suggestions would be greatly appreciated.
nodejs 16.13.0
react-router-dom 8.1.2
Issue
You are using/calling the useHistory hook from App but App is the very component rendering the BrowserRouter that provides the routing context to the app, including a valid history object reference.
Solution
Move the BrowserRouter out of App, likely into the index.js where you are rendering App. This is to provide a valid routing context for App and all other components in the App's subtree to access.
Example index.js
...
import { BrowserRouter } from "react-router-dom";
...
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
rootElement
);
App
function App() {
const { token, setToken } = useToken();
const [cart, setCart] = useState(GetCart());
const [isOpen, setIsOpen] = useState(false);
const history = useHistory(); // <-- will now be defined
...
const onRequestClose = () => {
setIsOpen(false);
setCart(GetCart);
history.push('/'); // <-- should navigate now
}
...
return (
<div className="App">
<p className="greeting">Hello, {token.firstName}</p>
<LogoHeader />
<GetCategories />
<Switch>
<Route path="/cart">
<Suspense fallback={<div>Loading...</div>}>
<>
<table className="table table-striped table-bordered">
...
</table>
<Button onClick={handleClick}>Checkout</Button>
{isOpen && <CheckoutModal onRequestClose={onRequestClose} />}
</>
</Suspense>
</Route>
...
</Switch>
<NewFooter />
</div>
);
}
Using useRouter in App.js will not work. Becourse its outside the browser router. BrowserRouter wrapped components can only able to use useRouter. Hope this is helpfull.
Related
I have this little projekt where I enter some user info into a form and on submit it will display it on the same page in a table. This seem to work as intended but then when I click on Home page and then back to the form page the list is empty. I would like the list to persist as long as you browse the site.
I've been struggling with this for a while and would love to get some help.
I'll post the components.
import { useState } from "react";
import { Route, Routes } from "react-router-dom";
import Navbar from "./Components/Navbar";
import Home from "./Components/Home";
import PersonList from "./Components/PersonList";
import PersonDetails from "./Components/PersonDetails";
import LoginForm from "./Components/LoginForm";
import { UserContext } from "./Components/UserContext";
import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";
function App() {
const [value, setValue] = useState(null);
return (
<div className="App">
<UserContext.Provider value={{value, setValue }}>
<Navbar />
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/PersonList" element={<PersonList />} />
<Route path="/Components/PersonDetails/:id/*" element={<PersonDetails />} />
<Route path="/LoginForm" element={<LoginForm />} />
</Routes>
</div>
</UserContext.Provider>
</div>
);
}
export default App;
import { Link } from "react-router-dom";
import { UserContext } from "./UserContext";
import { useContext, useState } from "react";
export default function Navbar() {
const {value, setValue} = useContext(UserContext);
return (
<nav className="navbar">
<h1>React Assignment</h1>
<div className="links">
<Link to="/">Home</Link>
<Link to="/PersonList">Person List</Link>
{ !value && <Link to="/LoginForm">Login</Link> }
{ value && <Link to={"/"} onClick={() => {setValue(null);}} >Logout</Link> }
{value}
</div>
</nav>
)
}
import { useState } from "react";
import UserForm from "./UserForm";
import UserList from "./UserList";
const PersonList = () => {
// create array using useState to store the contact form data.
const [contacts, updateContacts] = useState([]);
// update contact state.
const addContact = (contact) => {
updateContacts([...contacts, contact]);
};
return (
<div className="personlist">
<UserForm addContact={addContact} />
<UserList contacts={contacts} />
</div>
);
}
export default PersonList;
import { useState } from "react";
import { Form, Button } from "react-bootstrap";
// retrieve addContact function from PersonList.js as props.
export default function UserForm({ addContact }) {
// useState hook to manage state in functional components.
const [contactInfo, setContactInfo] = useState({
firstname: "",
lastname: "",
age: "",
nationality: "",
email: "",
});
// update input fields.
const handleChange = (event) => {
setContactInfo({ ...contactInfo, [event.target.name]: event.target.value });
};
// submit data to addContact function.
const handleSubmit = (event) => {
event.preventDefault();
addContact(contactInfo);
// clear input fields after submit
setContactInfo({
firstname: "",
lastname: "",
age: "",
nationality: "",
email: "",
});
};
return (
<div className="form-container w-50">
<Form onSubmit={handleSubmit}>
<h3>Contact Form</h3>
<Form.Control
size="sm"
type="text"
name="firstname"
placeholder="First Name"
required
value={contactInfo.firstname}
onChange={handleChange}
/>
<Form.Control
size="sm"
type="text"
name="lastname"
required
placeholder="Last Name"
value={contactInfo.lastname}
onChange={handleChange}
/>
<Form.Control
size="sm"
type="number"
name="age"
placeholder="Age"
value={contactInfo.age}
onChange={handleChange}
/>
<Form.Control
size="sm"
type="text"
name="nationality"
placeholder="Nationality"
value={contactInfo.nationality}
onChange={handleChange}
/>
<Form.Control
size="sm"
type="email"
name="email"
placeholder="Email"
required
value={contactInfo.email}
onChange={handleChange}
/>
<p></p>
<Button variant="primary" size="sm" type="submit">
Submit Contact
</Button>
<p></p>
</Form>
</div>
);
}
import { Table } from "react-bootstrap";
import uuid from "react-uuid";
import { Link } from "react-router-dom";
// retrieve contacts from PersonList.js as props
export default function UserList({ contacts }) {
if (contacts.length === 0) {
return <h5>The list is empty</h5>;
} else {
return (
<Table className="w-100" bordered hover size="sm">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Age</th>
<th>Nationality</th>
<th>Email</th>
</tr>
</thead>
<tbody className="tablelinks">
{/* use .map to loop */}
{contacts.map((contact) => (
<tr key={uuid()}>
<td>
<Link to={`/Components/PersonDetails/${contact.firstname+contact.lastname}`}>
{contact.firstname}
</Link>
</td>
<td>{contact.lastname}</td>
<td>{contact.age}</td>
<td>{contact.nationality}</td>
<td>{contact.email}</td>
</tr>
))}
</tbody>
</Table>
);
}
}
If you are just needing to persist the contacts state so it is available when navigating to the "/PersonList" path then the obvious solution is to Lift State Up to a common ancestor component that remains mounted while routed components are mounted/unmounted. The App component is this ancestor.
function App() {
const [value, setValue] = useState(null);
// create array using useState to store the contact form data.
const [contacts, updateContacts] = useState([]);
// update contact state.
const addContact = (contact) => {
updateContacts(contacts => [...contacts, contact]);
};
return (
<div className="App">
<UserContext.Provider value={{value, setValue }}>
<Navbar />
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/PersonList" element={<PersonList {...{ addContact, contacts }} />} />
<Route path="/Components/PersonDetails/:id/*" element={<PersonDetails />} />
<Route path="/LoginForm" element={<LoginForm />} />
</Routes>
</div>
</UserContext.Provider>
</div>
);
}
...
const PersonList = ({ contacts, addContact }) => {
return (
<div className="personlist">
<UserForm addContact={addContact} />
<UserList contacts={contacts} />
</div>
);
}
Additionally if you need to also persist the contacts state when the page is reloaded/refreshed then you'll need to persist the state to something more long-term. Initialize/persist the state from/to localStorage.
Example:
function App() {
const [value, setValue] = useState(null);
// create array using useState to store the contact form data.
const [contacts, updateContacts] = useState(
JSON.parse(localStorage.getItem("contacts")) ?? []
);
useEffect(() => {
localStorage.setItem("contacts", JSON.stringify(contacts));
}, [contacts]);
...
}
Child
import React from "react";
import { Link } from "react-router-dom";
import "../pages/All.css";
const MovieList = (props) => {
return(
<>
{props.movies.map((movie, index) => (
<div className="ro">
<img src={movie.Poster} alt={movie.Title} /> <br /> <br />
<label>{movie.Title}</label> <br />
<p>{movie.Type} {movie.Year}</p> <br /> <br />
<div className="overlay">
<li><Link to = "/play" class="popup-video btn">Watch</Link></li>
<li><Link to ="movie-details.html" class="btn">Download</Link></li>
</div>
</div>
))}
</>
);
};
export default MovieList;
import React from "react";
import ReactPlayer from "react-player";
function Play(movie){
return(
<div>
<ReactPlayer width="100%" height="750px" controls url ={"http://www.youtube.com/"+movie.Title} />
</div>
);
}
export default Play;
i am trying to pass {movie.Title} to controls url in Parent Component
I hope what you want to ask is how to pass params.
const MainNavigation = ()=> (
<Routes>
<Route path="/" element={<VideoList />} />
<Route path="/play:id" element={<Play />} />
</Routes>
)
const VideoList = ()=> (
<Link to={"/play/" + movie.title}>{movie.title}</Link>
)
const Play = ()=> {
const {id} = useParams();
return (
<VideoComponent url={"https://example.com/" + id} />
)
}
For more information, read official documents. https://reactrouter.com/docs/en/v6/hooks/use-params
I have some user components on which I used the navigation component and I am routing two components on it. when I click on the Device.js component button it should remove device.js and it should redirect to the Keyregister.js component which is having navigation component in it .It should change the value according to the props sent to it. But it is not happening.
user.js
import React from "react";
import { Route, Switch } from "react-router";
import "./User.css";
import Navigation from "./Dashboard/Navigation";
import AuthModel from "./Dashboard/AuthModel";
import DeviceDetails from "./Dashboard/DeviceDetails";
function User() {
return (
<>
<Navigation
link1={""}
link2={"Authmodel"}
link3={"Requests"}
link1name={"Key Status"}
link2name={"Key Upload"}
link3name={"KEY DOWNLOAD"}
/>
<Switch>
<Route path="/User/Dashboard/AuthModel" component={AuthModel} />
<Route path="/User/Dashboard/DeviceDetails" component={DeviceDetails} />
</Switch>
</>
);
}
export default User;
Navigation.js
import React from "react";
import "./Navigation.css";
import { Link } from "react-router-dom";
import { useHistory } from "react-router";
import { Image } from "react-bootstrap";
import EvoVert from "../../../Resources/EvoluteVert.png";
function Navigation(props) {
const history = useHistory();
const handleLogout = (e) => {
e.preventDefault();
localStorage.removeItem("accessToken");
localStorage.removeItem("roleType");
history.push("/");
};
return (
<>
<main classMain="main">
<aside className="sidebar ">
<Image className="Evo-logo" src={EvoVert} />
<nav className="nav">
<ul style={{ textDecoration: "none" }}>
<li>
<Link to={`/User/Dashboard/${props.link2}`}>
{props.link2name}
</Link>
</li>
<li>
<Link to={`/User/Dashboard/${props.link1}`}>
{props.link1name}
</Link>
</li>
<li>
<Link to={`/User/Dashboard/${props.link3}`}>
{props.link3name}
</Link>
</li>
</ul>
</nav>
</aside>
</main>
</>
);
}
export default Navigation;
Device.js
import React, { useState, useEffect } from "react";
import { Form } from "react-bootstrap";
import "./Dashboard.css";
import { useHistory } from "react-router";
import KeyRegister from "./KeyRegister";
function DeviceDetails() {
const history = useHistory();
const [visible, setvisible] = useState(true);
const [serialNum, setSerialNum] = useState("");
const [slot, setSlot] = useState("");
const frmsubmit = (e) => {};
return (
<>
<section className="device-Details_Dashboard">
<div className="container">
<div
className="heading"
style={{
color: "#312450",
fontWeight: "400",
fontSize: "35px",
padding: "1rem",
}}
>
DEVICE DETAILS
</div>
<Form onSubmit={frmsubmit}>
<div>
<div className="device-details-box">
<label>Serial Number</label>
<br />
<input
type="text"
value={serialNum}
onChange={(e) => setSerialNum(e.target.value)}
/>
</div>
<div className="device-details-box">
<label>Slot Number</label>
<br />
<input
type="text"
value={slot}
onChange={(e) => setSlot(e.target.value)}
/>
</div>
</div>
<button className="devDetSub" onClick={frmsubmit}>
<span>SUBMIT</span>
</button>
</Form>
</div>
</section>
{visible && <KeyRegister />}
</>
);
}
export default DeviceDetails;
KeyRegister.js
import React, { useState, useEffect } from "react";
import "./Navigation.css";
import { Route, Switch } from "react-router";
import DUPKT from "./DUPKT";
import MasterSession from "./MasterSession";
import AES from "./AES";
import Navigation from "./Navigation";
function KeyRegister() {
return (
<>
<Navigation
link1={"DUPKT"}
link2={"MasterSession"}
link3={"AES"}
link1name={"DUPKT"}
link2name={"MASTER KEY"}
link3name={"AES"}
/>
<main classMain="main">
<Switch>
<Route path="/User/Dashboard/DUPKT" component={DUPKT} exact />
<Route
path="/User/Dashboard/MasterSession"
component={MasterSession}
exact
/>
<Route path="/User/Dashboard/AES" component={AES} exact />
</Switch>
</main>
</>
);
}
export default KeyRegister;
I cannot see anywhere in your naviagation component props you are passing device details.
<Navigation
link1={""}
link2={"Authmodel"}
link3={"Requests"}
link1name={"Key Status"}
link2name={"Key Upload"}
link3name={"KEY DOWNLOAD"} //should have device details in props
/>
I am new to React js, I have tried to Build Login page in react js with navbar all went fine but when I have change the logout from login in navbar after successful login I was struck. I have tried multiple ways but none of the ways worked. Can anyone please help me solve my issue.
Login.js
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import '../App.css'
import UserPage from './UserPage';
function Login(props) {
let navi = useNavigate();
const [name, setName] = useState();
const [password, setPassword] = useState();
const [message, setMessage] = useState();
const [loggedIn, setLoggedIn] = useState(0);
const database = [
{
name: "piranesh",
password: "12"
}
];
function handleSubmit(event) {
event.preventDefault();
const userData = database.find((n) => n.name === name);
if (userData) {
if (userData.password === password) {
setLoggedIn(1);
navi('/userprofile');
}
else {
setMessage("Invalid username or Password");
}
}
else {
setMessage("Invalid username or Password");
}
setName("");
setPassword("");
}
return (
<div style={{ paddingTop: "150px" }}>
<h4>Login</h4>
<form onSubmit={handleSubmit}>
<h3>{message}</h3>
<table className="center">
<tr>
<th><label >User Name : </label></th>
<th><input type="text" name="name" value={name} placeholder="Enter your user name" onChange={(event) => setName(event.target.value)} /><br /></th>
</tr>
<tr>
<th><label >Password : </label></th>
<th><input type="password" name="password" value={password} placeholder="Enter your password" onChange={(event) => setPassword(event.target.value)} /><br /></th>
</tr>
<tr>
<th><button type="submit">Log in</button></th>
</tr>
</table>
</form>
</div>
);
}
export default Login;
Header.js
import React, { useState } from 'react';
import { Link, BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './Home';
import About from './About';
import Login from './Login';
import UserPage from './UserPage';
function Header(props) {
return (
<div>
<BrowserRouter>
<Link to="/" style={{ paddingRight: '60px', paddingLeft: '950px' }}>Home</Link>
<Link to="/about" style={{ paddingRight: '60px' }}>About</Link>
<Link to="/login" style={{ paddingRight: '60px' }}>Login</Link>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/login' element={<Login />} />
<Route path='/about' element={<About />} />
<Route path='/userprofile' element={<UserPage />} />
</Routes>
</BrowserRouter>
</div>
)
};
export default Header;
UserPage.js
import React from 'react';
function UserPage(props) {
return (
<div>
<h1>Hello this is User Page </h1>
</div>
)
};
export default UserPage;
App.js
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import Header from './components/Header';
function App() {
return (
<div className="App">
<Header />
</div>
);
}
export default App;
Thanks
The main problem is that this image is selected from the file explorer and I am using react-router.
Add.js
this is where you select the image
import { Link } from "react-router-dom";
import About from "./About";
import './styles/modal.css'
import firebase from "firebase";
require("firebase/firestore")
require("firebase/storage")
export default function Add(props) {
const [image, setImage] = useState(null);
const [modal, setModal] = useState(false);
const pickImage = (event) => {
//console.log();
setImage(URL.createObjectURL(event.target.files[0]));
//console.log(image);
}
const toggle = () => {
setModal(!modal);
}
return (
<div>
<h1>Add</h1>
<button onClick={toggle}>Add image</button>
{modal ? (
<div className="modal-bg">
<div className="modal">
<img src={image} style={{ width: '60%', height: '60%' }} className="ex-img" />
<br /><br />
<label htmlFor="fie" className="gcoo btn-default">+ File
<input id="fie" type="file" onChange={pickImage} className="file-pick" />
</label>
<br />
<br />
<div className="bottom-buttons">
<Link to="/about">
<button className="save">Save</button>
</Link>
<button onClick={() => setModal(false)} className="closse">Close</button>
</div>
</div>
</div>
) : null}
</div>
)
}
I am using firebase but not in this file so you can ignore this.
MainRoutes.js
this is where all the routes and pages are.
import { Route } from "react-router";
import { Switch } from 'react-router-dom';
import ImageDisplay from "./components/ImageDisplay";
import Add from './components/Add';
export default function MainRoute(props) {
return (
<Switch>
<Route exact path="/about" component={() => <ImageDisplay />} />
<Route exact path="/add" component={() => <Add />} />
</Switch>
);
}
finally this file ImageDisplay.js is where the image should be displayed
I dont have much on this file because i dont know how to put in any images.
I have tried props but whenever i imported the Imagedisplay it always showed the content on it and i dont want anything from image display. I only want to send an image over there.
import React from 'react'
import { Link } from "react-router-dom"
function ImageDisplay(props) {
return (
<div>
<h1>image</h1>
<div>
<img />
<p></p>
</div>
</div>
)
}
export default ImageDisplay;
Make sure that you are not reloading the page when moving between the two routes, since this will remove all state.
To share state between the two components you would need to store the state in the parent, and pass it to the children.
Add:
export default function Add(props) {
const pickImage = (event) => {
props.setImage(URL.createObjectURL(event.target.files[0]));
}
// The rest of your function
}
And in your parent:
export default function MainRoute(props) {
const [image, setImage] = useState(null)
return (
<Switch>
<Route exact path="/about" component={() => <ImageDisplay image={image} />} />
<Route exact path="/add" component={() => <Add setImage={setImage} />} />
</Switch>
);
}
You can now access the image prop in your imageDisplay.
function ImageDisplay(props) {
return (
<div>
<h1>image</h1>
<div>
<img src={props.image} />
<p></p>
</div>
</div>
)
}