React Router not linking to page - reactjs

Fairly new to react and trying to get the router to work for me. I'm pulling in data from a third party api using a simple form. When the data is retrieved I'd like to display it in a new route, and then ultimately have another route for each item retrieved. When I submit the form I just get a '?' in the route params. If I enter the route manually then submit the form the data displays. How can I get the data to display on form submit?
import axios from "axios";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import "./App.css";
import RecipeList from "./RecipeList";
import Header from "./Header";
function App() {
const [recipes, setRecipes] = useState([]);
const [query, setQuery] = useState("");
const [search, setSearch] = useState("");
const APP_ID = "XXXXXXXX";
const APP_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const url = `https://api.edamam.com/api/recipes/v2?type=public&q=${query}&app_id=${APP_ID}&app_key=${APP_KEY}`;
const getRecipes = async () => {
const res = await axios(url);
const data = await res.data.hits;
console.log(data);
setRecipes(data);
};
useEffect(() => {
getRecipes();
}, [query]);
const updateSearch = (e) => {
setSearch(e.target.value);
console.log(search);
};
const getSearchQuery = () => {
e.preventDefault();
setQuery(search);
setSearch("");
};
return (
<div className="App">
<Header />
<div>
<div className="container">
<form className="search-form" onSubmit={getSearchQuery}>
<input
className="search-input"
type="text"
value={search}
onChange={updateSearch}
placeholder="search by food name"
/>
<button className="search-button" type="submit">
Search Recipes
</button>
</form>
</div>
</div>
{/* <RecipeList recipes={recipes}/> */}
<Router>
<Routes>
<Route path="/recipes" element={<RecipeList recipes={recipes}/>} />
<Route path="/recipes/:id" />
</Routes>
</Router>
</div>
);
}
export default App;
import React from "react";
import { Link } from "react-router-dom";
const RecipeList = ({ recipes }) => {
console.log(recipes);
return (
<div>
{recipes.map(({ recipe }, id) => (
<Link to={`recipes/${recipe.label}`}>
<p key={id}>{recipe.label}</p>
</Link>
))}
</div>
);
};
export default RecipeList;

If I understand your question/issue you are having issue linking to a specific recipe. I suspect it is because you are using a relative link, so you are linking to a "/recipes/recipes/<label".
Either use absolute link paths, i.e. using a leading "/":
<Link to={`/recipes/${recipe.label}`}>
<p key={id}>{recipe.label}</p>
</Link>
Or use a correct relative path, i.e. only append the next level path segment, in other words, appending recipe.label to "/recipes":
<Link to={`${recipe.label}`}>
<p key={id}>{recipe.label}</p>
</Link>
If you wanting to start on "/" and submit the form and navigate to "/recipes" then issue an imperative navigation after submitting the form. Import the useNavigate hook to issue the imperative navigation and move the Router to wrap the App component so the routing context is provided to it and the useNavigate hook can work properly.
import axios from "axios";
import { BrowserRouter as Router, Routes, Route, useNavigate } from "react-router-dom";
import "./App.css";
import RecipeList from "./RecipeList";
import Header from "./Header";
function App() {
const navigate = useNavigate(); // <-- use navigate hook
const [recipes, setRecipes] = useState([]);
const [query, setQuery] = useState("");
const [search, setSearch] = useState("");
const APP_ID = "XXXXXXXX";
const APP_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const url = `https://api.edamam.com/api/recipes/v2?type=public&q=${query}&app_id=${APP_ID}&app_key=${APP_KEY}`;
const getRecipes = async () => {
const res = await axios(url);
const data = await res.data.hits;
console.log(data);
setRecipes(data);
};
useEffect(() => {
getRecipes();
}, [query]);
const updateSearch = (e) => {
setSearch(e.target.value);
console.log(search);
};
const getSearchQuery = () => {
e.preventDefault();
setQuery(search);
setSearch("");
navigate("/recipes"); // <-- imperative navigation
};
return (
<div className="App">
<Header />
<div>
<div className="container">
<form className="search-form" onSubmit={getSearchQuery}>
<input
className="search-input"
type="text"
value={search}
onChange={updateSearch}
placeholder="search by food name"
/>
<button className="search-button" type="submit">
Search Recipes
</button>
</form>
</div>
</div>
<Routes>
<Route path="/recipes" element={<RecipeList recipes={recipes}/>} />
<Route path="/recipes/:id" />
</Router>
</div>
);
}
index.js
<Router>
<App />
</Router>

Related

Whenever I Put the payment page on my app, it goes blank on ReactJS

I'm doing an e-commerce app in which you rent tuxedos but when I go to the payments page it goes blank. It happened when I installed Stripe API on my app and it became buggy in the specific page. In this version of React, I tried to put on the payment page but it goes blank. Can you guys help me solve this problem please?
Here's my code on App.js:
import './App.css';
import Header from './Header.js';
import Home from './Home.js';
import Checkout from './Checkout.js';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Login from './Login';
import { useEffect } from 'react';
import { auth } from './firebase';
import { useStateValue } from './StateProvider';
import Payment from './Payment';
import { loadStripe } from '#stripe/stripe-js';
import { Elements } from '#stripe/react-stripe-js';
const promise = loadStripe('some stripe api here');
function App() {
const [{}, dispatch] =useStateValue();
useEffect(() => {
//Only run once the app component logs
auth.onAuthStateChanged(authUser => {
console.log('User is signed in', authUser)
if (authUser) {
dispatch({
type:'SET_USER',
user: authUser
})
} else {
dispatch({
type:'SET_USER',
user: null
})
}
})
}, [])
return (
//BEM
<Router>
<div className="app">
<Routes>
<Route path="/login" element={[<Login />]}/>
<Route path="/checkout" element={[<Header />, <Checkout />]}/>
<Route path="/payment" element={[<Header />, <Elements stripe={promise} />, <Payment />]}/>
<Route path="/" element={[<Header />, <Home />]}/>
</Routes>
</div>
</Router>
);
}
export default App;
Now here's my code on the Payment page (Payment.js):
import { CardElement, useElements, useStripe } from '#stripe/react-stripe-js';
import React, { useEffect, useState } from 'react';
import CurrencyFormat from 'react-currency-format';
import { Link, useNavigate } from 'react-router-dom';
import CheckoutProduct from './CheckoutProduct';
import './Payment.css';
import { useStateValue } from './StateProvider';
import { getCartTotal } from './reducer';
import axios from 'axios';
function Payment() {
const [{cart, user}, dispatch] = useStateValue();
const navigate = useNavigate();
const stripe = useStripe();
const elements = useElements();
const [succeeded, setSucceeded] = useState(false);
const [processing, setProcessing] = useState("");
const [error, setError] = useState(null);
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState(true);
useEffect(() => {
const getClientSecret = async() => {
const response = await axios({
method: 'post',
url: `/payments/create?total=${getCartTotal(cart) * 100}`
});
setClientSecret(response.data.clientSecret)
}
getClientSecret();
}, [cart])
const handleSubmit = async(event) => {
event.preventDefault();
setProcessing(true);
const payload = await stripe.confirmCardPayment(clientSecret, {
payment_method : {
card: elements.getElement(CardElement)
}
}).then(({paymentIntent}) => {
setSucceeded(true);
setError(null)
setProcessing(false)
navigate('/orders', {replace:true});
})
}
const handleChange = event => {
setDisabled(event.empty);
setError(event.error ? event.error.message : '');
}
return (
<div className='payment'>
<div className='payment_container'>
<h1> Checkout (<Link to='/checkout'> {cart?.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> 123 Elvis Lane </p>
<p> Austin, Texas </p>
</div>
</div>
{/* Payment section - Review items */}
<div className='payment_section'>
<div className='payment_title'>
<h3> Review items and delivery </h3>
<div className='payment_items'>
{cart.map(item => (
<CheckoutProduct
id = {item.id}
title = {item.title}
image = {item.image}
price = {item.price}
rating = {item.rating}
/>
))}
</div>
</div>
</div>
{/* Payment section - Payment method */}
<div className='payment_section'>
<div className='payment_title'>
<h3> Payment Method </h3>
<div className='payment_details'>
{/* Stripe API */}
<form onSubmit={handleSubmit}>
<CardElement onChange={handleChange} />
<div className='payment_priceContainer'>
<CurrencyFormat
renderText={(value) => (
<>
<h3> Order Total: {value} </h3>
</>
)}
decimalScale={2}
value= {getCartTotal(cart)}
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>
</div>
)
}
export default Payment
Is this an error on App.js or is it in Payment.js? The page should display the info and the payment form.
Edit: I found out it was in the Payment.js code somewhere around here:
const navigate = useNavigate();
const stripe = useStripe();
const elements = useElements();
const [succeeded, setSucceeded] = useState(false);
const [processing, setProcessing] = useState("");
const [error, setError] = useState(null);
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState(true);
useEffect(() => {
const getClientSecret = async() => {
const response = await axios({
method: 'post',
url: `/payments/create?total=${getCartTotal(cart) * 100}`
});
setClientSecret(response.data.clientSecret)
}
getClientSecret();
}, [cart])
const handleSubmit = async(event) => {
event.preventDefault();
setProcessing(true);
const payload = await stripe.confirmCardPayment(clientSecret, {
payment_method : {
card: elements.getElement(CardElement)
}
}).then(({paymentIntent}) => {
setSucceeded(true);
setError(null)
setProcessing(false)
navigate('/orders', {replace:true});
})
}
const handleChange = event => {
setDisabled(event.empty);
setError(event.error ? event.error.message : '');
Can you guys help me fix this please? It seems that in this section is where the error is occurring.
Edit 2:
Here's of how it should look like:
Here's what actually happens:
Edit 3: Here's what the console gives me as an error, maymbe it is in the elements tag that causes the problem.
It looks like you need to wrap your checkout page in an Elements provider:
To use the Payment Element component, wrap your checkout page component in an Elements provider. Call loadStripe with your publishable key, and pass the returned Promise to the Elements provider. Also pass the client secret from the previous step as options to the Elements provider.
The sample code Stripe provides shows how to properly structure your app:
import React from 'react';
import ReactDOM from 'react-dom';
import {Elements} from '#stripe/react-stripe-js';
import {loadStripe} from '#stripe/stripe-js';
import CheckoutForm from './CheckoutForm';
// Make sure to call `loadStripe` outside of a component’s render to avoid
// recreating the `Stripe` object on every render.
const stripePromise = loadStripe('pk_test_123');
function App() {
const options = {
// passing the client secret obtained in step 2
clientSecret: '{{CLIENT_SECRET}}',
// Fully customizable with appearance API.
appearance: {/*...*/},
};
return (
<Elements stripe={stripePromise} options={options}>
<CheckoutForm />
</Elements>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
import React from 'react';
import {PaymentElement} from '#stripe/react-stripe-js';
const CheckoutForm = () => {
return (
<form>
<PaymentElement />
<button>Submit</button>
</form>
);
};
export default CheckoutForm;

How to pass props through Link

Fairly new to react here. I'm making a small recipe finder app with an api. After getting the data, I'm mapping through the results and displaying them in a component. What I want to do is display the details of each recipe through another component in another route. I'm not sure how to do this. I thought I could pass the mapped recipe through Link, but it's not working. Here is what I have so far.
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>,
document.getElementById('root')
);
App.js
import React, { useState, useEffect} from "react";
import axios from "axios";
import { BrowserRouter as Router, Routes, Route, useNavigate} from "react-router-dom";
import "./App.css";
import RecipeList from "./RecipeList";
import Recipe from "./Recipe";
import Header from "./Header";
function App() {
const navigate = useNavigate();
const [recipes, setRecipes] = useState([]);
const [query, setQuery] = useState("");
const [search, setSearch] = useState("");
const APP_ID = "XXXXXXXXX";
const APP_KEY = "XXXXXXXXXXXXXXXXXXXXXX";
const url = `https://api.edamam.com/api/recipes/v2?type=public&q=${query}&app_id=${APP_ID}&app_key=${APP_KEY}`;
const getRecipes = async () => {
const res = await axios(url);
const data = await res.data.hits;
console.log(data);
setRecipes(data);
};
useEffect(() => {
getRecipes();
}, [query]);
const updateSearch = (e) => {
setSearch(e.target.value);
console.log(search);
};
const getSearchQuery = (e) => {
e.preventDefault();
setQuery(search);
setSearch("");
navigate("/recipes");
};
return (
<div className="App">
<Header />
<div>
<div className="container">
<form className="search-form" onSubmit={getSearchQuery}>
<input
className="search-input"
type="text"
value={search}
onChange={updateSearch}
placeholder="search by food name"
/>
</form>
</div>
</div>
<Routes>
<Route path="/recipes" element={<RecipeList recipes={recipes} />} />
<Route path="/recipes/:id" element={<Recipe recipes={recipes} />}/>
</Routes>
</div>
);
}
export default App;
RecipeList.jsx
import React from "react";
import { Link } from "react-router-dom";
const RecipeList = ({ recipes }) => {
return (
<div className="container">
<div className="grid-container">
{recipes.map(({ recipe }) => (
<Link to={`/recipes/${recipe.label}`}>
<img key={recipe.image} src={recipe.image} alt="" />
<p key={recipe.label}>{recipe.label}</p>
<p>{recipe.id}</p>
</Link>
))}
</div>
</div>
);
};
export default RecipeList;
Recipe.jsx
const Recipe = ({recipe}) => {
return (
<div>
<h1>{recipe.label}</h1>
</div>
)
}
export default Recipe
Am I even close???
You are passing the entire recipes array to both routed components.
<Routes>
<Route path="/recipes" element={<RecipeList recipes={recipes} />} />
<Route path="/recipes/:id" element={<Recipe recipes={recipes} />}/>
</Routes>
So Recipe can use the entire array and the id route match param to search the passed array and render the exact recipe by matching label.
import { useParams } from 'react-router-dom';
const Recipe = ({ recipes }) => {
const { id } = useParams();
const recipe = recipes.find(recipe => recipe.label === id); // *
return recipe ? (
<div>
<h1>{recipe.label}</h1>
</div>
) : null;
};
* Note: Since you call the route param id it may make more sense to us the recipe.id for the link.
{recipes.map(({ recipe }) => (
<Link to={`/recipes/${recipe.id}`}>
<img key={recipe.image} src={recipe.image} alt="" />
<p key={recipe.label}>{recipe.label}</p>
<p>{recipe.id}</p>
</Link>
))}
...
const recipe = recipes.find(recipe => recipe.id === id);

React Context data got undefined everytime refresh page

I am making a simple SPA where you need to login before you can access other pages. I can successfully login and store the login data (firstname, lastname, etc.) cause I plan to use the data again later in the other pages. The problem is whenever I refresh the page, it always empty the state in the context which cause me to return to the login page. I am referring link for my SPA.
Do I need to do this? I would be thankful if someone can point out what I should change / improve. Thank you.
Here is my code.
App.js
import React, { useState } from "react";
import { BrowserRouter as Router, Link, Route } from "react-router-dom";
import { AuthContext } from "./context/auth";
import PrivateRoute from "./PrivateRoute";
import Login from "./pages/Login";
import Signup from "./pages/Signup";
import Home from "./pages/Home";
import Admin from "./pages/Admin";
function App() {
const [authTokens, setAuthTokens] = useState();
const setTokens = (data) => {
// console.log("DATA ",data);
localStorage.setItem("tokens", JSON.stringify(data));
setAuthTokens(data);
}
// console.log(authTokens);
return (
<AuthContext.Provider value={{ authTokens, setAuthTokens: setTokens }}>
<Router>
<div className="app">
<ul>
<li><Link to="/">Home Page</Link></li>
<li><Link to="/admin">Admin Page</Link></li>
</ul>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={Signup} />
<Route exact path="/" component={Home} />
<PrivateRoute exact path="/admin" component={Admin} />
</div>
</Router>
</AuthContext.Provider>
);
}
export default App;
Login.js
import React, { useState } from "react";
import axios from "axios";
import { Link, Redirect } from "react-router-dom";
import { useAuth } from "../context/auth";
import { Card, Form, Input, Button, Error } from "../components/AuthForm";
const Login = () => {
const [isLoggedIn, setLoggedIn] = useState(false);
const [isError, setIsError] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const { setAuthTokens } = useAuth();
const handleLogin = () => {
axios
.post("LOGINLINK", {
email,
password,
})
.then((result) => {
if (result.status === 200) {
setAuthTokens(result.data);
setLoggedIn(true);
} else {
setIsError(true);
}
})
.catch((error) => {
setIsError(true);
});
};
if (isLoggedIn) {
return <Redirect to="/" />;
}
return (
<Card>
<Form>
<Input
type="email"
placeholder="Email"
value={email}
onChange={(e) => {
setEmail(e.target.value);
}}
/>
<Input
type="password"
placeholder="password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
}}
/>
<Button onClick={handleLogin}>Login</Button>
</Form>
<Link to="/signup">Don't have an account?</Link>
{isError && (
<Error>The username or password provided were incorrect!</Error>
)}
</Card>
);
};
export default Login;
Auth.js
import { createContext, useContext } from "react";
export const AuthContext = createContext();
export function useAuth() {
console.log("CONTEXT", useContext(AuthContext));
return useContext(AuthContext);
}
In your App component you need to fetch the data from localStorage when initializing your state so it has some data to start with.
const localToken = JSON.parse(localStorage.getItem("tokens"));
const [authTokens, setAuthTokens] = useState(localToken);
If user has already authenticated it will be available in localStorage else it's going to be null.
I also had same problem but I solved liked this Don't use localStorage directly use your state and if it is undefined then only use localStorage. cause directly manipulating state with localStorage is in contrast with react internal state and effects re-render .
const getToken = () => {
JSON.parse(localStorage.getItem('yourtoken') || '')
}
const setToken = (token) => {
localStorage.setItem('key' , token)
}
const [authTokens, setAuthTokens] = useState(getToken());
const setTokens = (data) => {
// console.log("DATA ",data);
setToken(token);
setAuthTokens(data);
}

how to update data from child to parent using hooks at login from database

Hi I have just started using hooks and I'm trying to pass data I got from my login page to the parent so the user can access his/her pages, but I don't know how to do that I used this guide here
But this just gave me a is not a function error
and I have no idea why
here is my app.js
import React, { useState, useEffect } from 'react';
import {BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import {authenticationService} from '../services/authentication.service';
import Home from '../Pages/Desktop/Desktop';
import Login from '../Pages/Login_Register/Login';
import Register from '../Pages/Login_Register/Register';
import history from '../history';
const App = (props) => {
const [currentUser, setCurrentUser] = useState(null);
const [isAdmin, setIsAdmin] = useState(null);
const [isVIP1, setIsVIP1] = useState(false);
const [name, setName] = useState(null);
const [id, setId] = useState('');
useEffect(() => {
authenticationService.currentUser.subscribe(z => {
setCurrentUser(z)
});
}, [])
return (
<div history={history}>
<Router>
<div>
<Switch>
<Route path="/register">
<Register />
</Route>
<Route path="/login">
<Login firstName={fornavn => setName(fornavn)} user_id={id => setId(id)} admin={admin => setIsAdmin(admin)} vip={vip1 => setIsVIP1(vip1)} />
</Route>
<Route path="/home">
<Home />
</Route>
</Switch>
</div>
</Router>
</div>
);
}
export default App;
and this is my login.js
import React, { useState } from 'react';
import {authenticationService} from '../../services/authentication.service';
import { Redirect } from 'react-router'
import { useForm } from 'react-hook-form';
export default function Login({firstname, user_id, admin, vip, props}) {
const [currentUser, setCurrentUser] = useState(null);
const [isAdmin, setIsAdmin] = useState(null);
const [isVIP1, setIsVIP1] = useState(false);
const [name, setName] = useState(null);
const [id, setId] = useState('')
const [submitted, setSubmitted] = useState(false);
const { register, handleSubmit, errors } = useForm();
if (submitted) {
return <Redirect push to={{
pathname: '/home'
}}
/>
}
let updname = null;
const onSubmit=(data) => {
authenticationService.login(data)
.then(
user => {
setSubmitted(true)
updname = user.fornavn;
},
error => {
console.log(error);
}
);
setName(updname)
firstname(updname)
}
return (
<div>
<h2>log ind</h2>
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<div>
<input name="email" type="text" ref={register({required: true})}/>
</div>
<div className="form-group">
<input name="password" type="password" ref={register({required: true})} />
</div>
<div>
<button type="submit"/>logind</button>
</div>
</div>
</form>
</div>
)
}
This is the easiest solution so for but unfortunately it doesn't work anyone that can tell me why or help me find another way to make it work?
It looks like you have the prop defined as firstName in the parent component, and firstname in the child component? If that's accurate, it would cause an error.
As a side note, the way you name your functions does not make it very obvious that they are in fact functions. I would name the function firstname something like handleUpdateFirstName or onChangeFirstName for example. Or...better yet, just pass setName as a prop. You are essentially just redefining it here:
firstName={fornavn => setName(fornavn)}
I would look into using Proptypes for your components - it will tell you if you have a missing or mislabeled prop.
Edit: example of passing setState as a prop:
const Child = ({name, setName}) => (
<div>
<h1>Edit Name</h1>
<input value={name} onChange={e => setName(e.target.value)}/>
</div>
)
const Parent = () => {
const [ name, setName ] = useState('')
return (
<Child name={name} setName={setName}/>
)
}
Okay the entire thing started with that I got an error saying
TypeError: Cannot read property 'fornavn' of null
This made no sense to me because when I console.log the whole thing fornavn (fistname in danish) was there
useEffect(() => {
authenticationService.currentUser.subscribe(z => {
console.log(z)
setCurrentUser(z);
setName(z.fornavn)
});
}, [])
so I tried to get the response from the login function witch gave me even more problems the solution to my problem was however to set an if statement in the useEffect like so:
useEffect(() => {
authenticationService.currentUser.subscribe(z => {
if(z === null){
setCurrentUser(null);
setName(null);
}else{
setCurrentUser(z);
setName(z.fornavn);
}
});
}, [currentUser])
And now it works..

React createContext/useContext does not survive between pages

I am trying to create a shared global state for all components that an app needs, and instead of relying on props drilling or redux, I am trying to achieve that with the React Context.
Why does my user context not survive when I switch between routes? The application bellow illustrates the issue.
Do I need to use any other hook in conjunction with useContext?
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { AuthenticationProvider } from "./AuthenticationProvider";
const Index = () => {
return (
<AuthenticationProvider>
<App />
</AuthenticationProvider>
);
}
ReactDOM.render(<Index />, document.getElementById('root'));
//App.js
import React, { useState, useContext } from 'react';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import './App.css';
import { AuthenticationContext } from './AuthenticationProvider';
function AddUser() {
const [formUser, setFormUser] = useState("");
const [user, setUser] = useContext(AuthenticationContext);
const handleSubmit = async (event) => {
event.preventDefault();
setUser(formUser);
}
return (
<React.Fragment>
Form user: {formUser}.
<form id="form1" onSubmit={handleSubmit}>
<input type="text" id="user" onChange={event => setFormUser(event.target.value)} />
<input type="submit" value="Save" />
</form>
<br/>
Current user: {user}
<br/>
Back to home
</React.Fragment>
);
}
function Home() {
const [user, setUser] = useContext(AuthenticationContext);
return (
<React.Fragment>
<div className="App">
Hello {user}.
<br/>
Add user
</div>
</React.Fragment>
);
}
function App() {
return (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/add" component={AddUser} />
</Switch>
</Router>
);
}
export default App;
//AuthenticationProvider.js
import React, { useState, createContext } from "react";
const DEFAULT_STATE = "";
export const AuthenticationContext = createContext(DEFAULT_STATE);
export const AuthenticationProvider = ({ children }) => {
const [user, setUser] = useState(DEFAULT_STATE);
return (
<AuthenticationContext.Provider value={[user, setUser]} >
{children}
</AuthenticationContext.Provider>
);
}
The problem is that you used a regular <a> link to navigate through the app and every time you go from Home to addUser the app refreshes. To navigate through the app without refreshing the page use the Link component from react-router-dom
in Home and AddUser change the a links to the Link component
import { Link } from "react-router-dom";
function Home() {
const { user, setUser } = useContext(AuthenticationContext);
return (
<React.Fragment>
<div className="App">
Hello {user}.
<br />
<Link to="/add">Add user</Link> <-- Changed a to Link
</div>
</React.Fragment>
);
}
function AddUser() {
const [formUser, setFormUser] = useState("");
const [user, setUser] = useContext(AuthenticationContext);
const handleSubmit = async (event) => {
event.preventDefault();
setUser(formUser);
}
return (
<React.Fragment>
Form user: {formUser}.
<form id="form1" onSubmit={handleSubmit}>
<input type="text" id="user" onChange={event => setFormUser(event.target.value)} />
<input type="submit" value="Save" />
</form>
<br />
Current user: {user}
<br />
<Link to="/">Back to home</Link> <-- Changed a to Link
</React.Fragment>
);
}

Resources