I have a react js app with react-router-dom v6 to handle the routes. The routes functionality worked just fine before i added firebase firestore, but now for some reason when i'm in the seeker page and i reload it, the home page gets rendered. It's not a problem with user authentication because that's handled by the login component, but i couldn't find the problem in my configuration.
This is my app component.
import React, {useEffect, useState} from "react";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { createBrowserHistory } from 'history';
import ReactDOM from "react-dom";
import "./styles/style.scss";
import Home from "./pages/home";
import Seeker from "./pages/seeker";
import NotFound from "./pages/notFound";
import Login from "./pages/login";
const App = () => {
const [token, setToken] = useState(false);
useEffect(() => {
if(localStorage.getItem("token") === null){
setToken(false);
} else {
setToken(true);
}
}, [])
return (
<BrowserRouter history={createBrowserHistory}>
<Routes>
<Route exact path="/" element={<Login isAuth={token}/>}/>
<Route exact path="/home" element={token ? <Home /> : <Navigate to="/"/>}/>
<Route exact path="/seeker" element={token ? <Seeker /> : <Navigate to="/"/>}/>
<Route path="*" element={<NotFound />}/>
</Routes>
</BrowserRouter>
)
}
ReactDOM.render(
<App />,
document.getElementById("root"))
The login component sets the token on localstorage correctly and the routes use it to render conditionally the components. But when i'm in the "/seeker" url the refresh takes me to "/home".
import React, { useState, useEffect } from "react";
import { Formik, Form, Field, ErrorMessage } from 'formik';
import axios from "axios";
require("babel-core/register");
require("babel-polyfill");
import GridRender from "../components/gridRender";
import { NavBar } from "../components/navBar";
async function getHeros(name) {
return await axios.get(
`https://www.superheroapi.com/api.php/${name}`
);
}
const Seeker = () => {
const [searchedHero, setSearchedHero] = useState("")
const [matchedHeros, setMatchedHeros] = useState([]);
useEffect(() => {
let isMounted = true;
if (isMounted) {
getHeros(searchedHero).then((heros) => {
const hero = heros.data.response != "error" ? heros.data.results : []
localStorage.setItem("addHeroAction", "true");
setMatchedHeros(hero);
}, console.error);
}
return () => { isMounted = false };
}, [searchedHero]);
return (
<div>
<NavBar />
<div className="seeker">
<h1 className="title">Find your next teammate!</h1>
<Formik /> //// here is a large form that i erased so it doesn't get long
{matchedHeros.length != 0 && <GridRender matchedHeros = {matchedHeros}/>}
</div>
</div>
)
}
export default Seeker;
And here is my firebase config (i use version 9) in case it's important because the bug appeared after firebase was implemented.
import { initializeApp } from "firebase/app";
import "regenerator-runtime/runtime";
import { addDoc, deleteDoc, doc } from 'firebase/firestore';
import { getFirestore, collection } from 'firebase/firestore'
const firebaseConfig = {
apiKey: "xxxxxxxxxxxxxxxxxx",
authDomain: "xxxxxxxxxxxxxxxxxxxx",
databaseURL: "xxxxxxxxxxxxxxxxxxxxx",
projectId: "superteam-maker",
storageBucket: "xxxxxxxxxxxxxxx",
messagingSenderId: "xxxxxxxxxxxxx",
appId: "xxxxxxxxxxxxxxxxxxxxxxxxxx"
};
initializeApp(firebaseConfig);
const db = getFirestore();
const colRef = collection(db, "team")
const addHero = (hero) => {
addDoc(colRef, {
name : hero.name,
powerstats :{
combat: parseInt(hero.powerstats.combat),
durability: parseInt(hero.powerstats.durability),
intelligence: parseInt(hero.powerstats.intelligence),
power: parseInt(hero.powerstats.power),
speed: parseInt(hero.powerstats.speed),
strength : parseInt(hero.powerstats.strength)
},
id: parseInt(hero.id),
image: {url : hero.image.url},
} )
}
const deleteHero = (hero) => {
const docRef = doc(db, "team", hero.id);
deleteDoc(docRef);
}
export {addHero, deleteHero, colRef}
////////UPDATE/////////
The login component autehnticates the user with a test API that returns a random token, so the user can only enter if a certain email and password get subbmited. If the auth is successfull the user gets rendered to "/home".
Here's the code fro the login component.
import React, {useEffect, useState} from "react";
import { Formik } from 'formik';
import { Navigate } from 'react-router-dom';
import axios from "axios";
const Login = (props) => {
const [token, setToken] = useState(false);
useEffect(() => {
if(localStorage.getItem("token") === null){
setToken(false);
} else {
setToken(true);
}
}, [])
const verification = () => {
if( token && props.isAuth){
return <Navigate to="/home"/>
}
}
let logInError = {};
return(
<div className="login__background">
<div className="login container">
{verification()}
<h1 className="display-1 title login__title">Make a team as <span>powerfull</span> as you!</h1>
<h5 className="login-title__sub">Please log in to create your superteam</h5>
<Formik
initialValues={{ email: '', password: '' }}
validate={values => {
const errors = {};
if (!values.email) {
errors.email = 'Required';
} else if (
!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
) {
errors.email = 'Invalid email address';
}
if (!values.password){
errors.password = "Required";
}
return errors;
}}
onSubmit={(values) => {
axios.post("http://challenge-react.alkemy.org/", values)
.then(response => {
let token = response.data.token
localStorage.setItem("token", token);
localStorage.setItem("addHeroAction", false);
setToken(true);
return <Navigate to="/home"/>
})
.catch(error => {
if (error.response) {
logInError = error.response.data.error;
window.alert(logInError);
} else if (error.request) {
console.log(error.request);
} else {
console.log('Error', error.message);
}
console.log(error.config);
})
}}>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit
}) => (
<form onSubmit={handleSubmit}>
<label className="form-label label" name="email">Email</label>
<br />
<input
className="form-control form-control-lg input"
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{errors.email && touched.email && errors.email}
<br />
<label className="form-label label " name="password">Password</label>
<br />
<input
className="form-control form-control-lg input"
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
/>
{errors.password && touched.password && errors.password}
<br />
<button type="submit" className="btn btn-danger">
Submit
</button>
</form>
)}
</Formik>
</div>
</div>
)
}
export default Login;
It also gets an IsAuth propr from App.js in case it's the first time the user enters the page and there is no token saved.