React cannot preserv list between route changes - javascript

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]);
...
}

Related

How to send input from one React component to other?

I want to create a login page. When the user submits the form, the username should be rendered on the dashboard. I am unable to figure that out.
import React from "react";
import Footer from "./Footer";
import Back from "./Back";
import { Link } from "react-router-dom";
const Login = () => {
return (
<div>
<Back />
<div>
<form className="login-form">
<h1 className="form-heading">Login</h1>
<input
className="form-input"
type="text"
placeholder="Enter your Username"
/>
<input
className="form-input"
type="password"
placeholder="Enter your password"
/>
<button className="form-button">
<Link to={"/dashboard"}>Login</Link>
</button>
</form>
</div>
<Footer />
</div>
);
};
export default Login;
To share data between two components, the standard react approach is to lift the state up to a parent component, then pass it down (through props or context). For example:
const App = () => {
const [user, setUser] = useState();
return (
<Routes>
<Route path="/login" element={<Login setUser={setUser}/>}
<Route path="/dashboard" element={<Dashboard user={user}/>}
</Routes>
)
}
const Login = ({ setUser }) => {
return (
// ...
<Link to="/dashboard" onClick={() => setUser('bob')}
// ...
);
}
const Dashboard = ({ user }) => {
return (
<div>
Hello {user}!
</div>
)
}

Reactjs - Cant render functional component

I'm trying to render a functional component. But for some reason, I'm getting this sentence on the console You need to enable JavaScript to run this app.
This is App.js
import './App.css';
import Home from './components/pages/home/Home';
import SignUp from './components/pages/sign/SignUp';
import Checking from './components/pages/sign/Checking';
import Login from './components/pages/sign/Login';
import Summery from './components/pages/summery/Summery';
import UserList from './components/pages/users/Users';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import UploadFiles from './components/pages/files/UploadFiles';
function App() {
return (
<Router>
<Routes>
<Route exact path='/' element= {<Home />}/>
<Route path='/sign-up' component= {Checking } />
<Route path='/log-in' element= {<Login />} />
<Route path='/admin' element= {<Summery />} />
<Route path='/users' element= {<UserList />} />
<Route path='/files' element= {<UploadFiles />} />
</Routes>
</Router>
);
}
export default App;
Home, Login, Summery, UserList and UploadFiles are React Components and not functions.
Only Checking is a function (Trying to convert everything to a function component).
They all render but only Checking logging into console You need to enable JavaScript to run this app.
This is Checking.js file
import React from "react";
import '../../../App.css';
import Button from '#material-ui/core/Button';
import SendIcon from '#material-ui/icons/Send';
import axios from 'axios';
import bcrypt from 'bcryptjs';
import Navbar from "../../navbar/Navbar";
import { useNavigate } from 'react-router-dom';
export default function Checking() {
const [companyID, setCompanyID] = React.useState('');
const [email, setEmail] = React.useState('');
const navigate = useNavigate();
function handleSubmit(Event) {
Event.preventDefault();
axios.post('/sign-up', {
companyID: companyID,
email: email})
.then(res =>{
//console.log(res.json());
console.log(res);
if(res.status === 200){
//this.app();
//history.push('/login');
}else if (res.status === 400){
console.log("duplicate ID");
}
}).catch(err =>{
// console.log("duplicate ID");
// console.log(err);
});
}
}
return (
<>
<Navbar />
<div className="register-container" ref={this.props.containerRef}>
<h1 className="sign-up">SIGN UP</h1>
<form onSubmit={handleSubmit}>
<div className="form">
<div className="form-group">
{/* <label htmlFor="company id">Company ID : </label> */}
<input
type="number"
name="companyID"
placeholder="Company id"
value={companyID}
onChange={(e) => setCompanyID(e.target.value)}
required/>
</div>
{ <div className="form-group">
{/* <label htmlFor="email">Email : </label> */}
<input
type="text"
name="email" placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required/>
</div> }
<div className="registerbtn">
{/* <button type="submit" class="btn btn-primary">Submit</button> */}
<Button type="submit" endIcon={<SendIcon />} color="primary" variant="contained">
Sign Up
</Button>
</div>
</div>
</form>
</div>
</>
);
}
What am I doing wrong?
Checking is also a react component and you need to render it like the others.
<Route path='/sign-up' element= {<Checking/> } />

No error showing on console, still CardELement is not rendering?

Coding a project using React. It's been 3 days I've looking for a solution. I had been learning coding and trying to integrate stripe payment option on my web-app. However, unable to get the proper result. Searched a lot, found no solution.
Code is below :
App.js
import { Elements } from '#stripe/react-stripe-js';
import { loadStripe } from '#stripe/stripe-js';
import { useEffect } from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import './App.css';
import Checkout from './Checkout';
import { auth } from './firebase';
import Header from './Header';
import Home from './Home';
import Login from './Login';
import Payment from './Payment';
import { useStateValue } from './StateProvider';
const promise = loadStripe("pk_test_51KtYdQSJwVy1hdNxUPD5HK8KcKbLDVSic6q6jfdPxuSLYiCjuwzBfE8Eo5u0zVclLgPP33InVTjAyM4rZP3ouWB9009AL3lsZ6");
function App() {
const [{ }, dispatch] = useStateValue();
useEffect(() => {
auth.onAuthStateChanged(authUser => {
console.log("THE USER IS >>>>> ", authUser);
if (authUser) {
dispatch({
type: "SET_USER",
user: authUser
});
}
else {
dispatch({
type: "SET_USER",
user: null
})
}
})
}, []);
return (
<BrowserRouter>
<div className="App">
<Header />
{console.log("load stripe>>>>>>>>",promise)}
<Routes>
<Route path="/payment" element={
<Elements stripe={promise}>
<Payment />
</Elements>
} />
<Route path="/login" element={<Login />} />
<Route path="/checkout" element={<Checkout />} />
<Route path="/" element={<Home />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
Component in which using Stripe and CardElement (Payment.js) :
import { CardElement, useElements, useStripe } from "#stripe/react-stripe-js";
import React, { useState } from "react";
import CurrencyFormat from "react-currency-format";
import { Link } from "react-router-dom";
import CheckoutProduct from "./CheckoutProduct";
import "./Payment.css";
import { getBasketTotal } from "./reducer";
import { useStateValue } from "./StateProvider";
function Payment() {
const [{ basket, user }, dispatch] = useStateValue();
const stripe = useStripe();
const elements = useElements();
const [error,setError]=useState(null);
const [disabled,setDisabled]=useState(true);
const [succeeded,setSucceeded]=useState(false);
const [processing,setProcessing]=useState("");
const handleSubmit = event => {
//do all the stripe stuff
event.preventDefault();
}
const handleChange = event =>{
// Listen for changes in the CardElement
// and display any errors as the customer types card details
setDisabled(event.empty);
setError(event.error ? event.error.message : "");
}
return (
<div className="payment">
<div className="payment__container">
<h1>
Checkout ({<Link to="/checkout">{basket?.length} items</Link>})
</h1>
{/* Payment section - delivery address */}
<div className="payment__section">
<div className="payment__title">
<h3>Delivery Address</h3>
</div>
<div className="payment__address">
<p>{user?.email}</p>
<p>786 Chaurangi Marg</p>
<p>Delhi, India</p>
</div>
</div>
{/* Payment section - Review items */}
<div className="payment__section">
<div className="payment__title">
<h3>Review Items and Delivery</h3>
</div>
<div className="payment__items">
{basket.map(item =>
<CheckoutProduct
id={item.id}
title={item.title}
price={item.price}
rating={item.raing} />
)}
</div>
</div>
{/* Payment section - payment method */}
<div className="payment__section">
<div className="payment__title">
<h3>Payment Method</h3>
</div>
<div className="payment__details">
<form onSubmit={handleSubmit}>
{/*<CardElement />*/}
<div className="card__element">
<CardElement />
</div>
<div className="payment__priceContainer">
<CurrencyFormat
renderText={(value)=>(
<>
<h3>Order Total: {value}</h3>
</>
)}
decimalScale={2}
value={getBasketTotal(basket)}
displayType={"text"}
thousandSeparator={true}
prefix={"₹"}
/>
<button disabled={processing || disabled || succeeded}>
<span>{processing ? <p>Processing</p> : "Buy Now"}</span>
</button>
</div>
{error && <div>{error}</div> }
</form>
</div>
</div>
</div>
</div>
)
}
export default Payment;

How to change as logout in navbar after successful login in react js?

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

How to force a route in React

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.

Categories