Reputation: 1287
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
Upvotes: 2
Views: 784
Reputation: 202608
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.
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>
);
}
Upvotes: 2
Reputation: 179
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.
Upvotes: 0