useeffect hook only runs when page refresh in a react-web-app - use-effect

As in the question, useeffect hook only runs when I refresh the page in a react-web-app. Here is the main App.js:
import Chat from './Chat';
function App() {
return (
<div className="app">
<div className="app__body">
<Router>
<Sidebar />
<Switch>
<Route path='/rooms/:roomId'>
<Chat />
</Route>
<Route path="/">
<Chat />
</Route>
</Switch>
</Router>
</div>
</div>
);
}
And here is the Chat.js component:
function Chat() {
const [roomName, setRoomName] = useState("");
const { roomId } = useParams();
useEffect(() => {
if(roomId) {
db.collection("rooms").doc(roomId).
onSnapshot(snapshot => (
setRoomName(snapshot.data().name)
))
}
}, [roomId]);
return ( ......my divs )
}
When I click the roomNames on the sidebar, the page link instantly changes without refresh, I mean the "roomId", but it doesn't enter into useEffect hook without refreshing the page, therefore it doesn't change the displayed roomName lively in the app.
Any ideas?

Related

Uncaught Error: A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>

After implemented Private Route I keep getting this error.
The problem appears when Im trying to get to the Profile page that nested in the .
App.js
import './App.css';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import LoginPage from './pages/LoginPage';
import SignUpPage from './pages/SignUpPage';
import ProfilePage from './pages/ProfilePage';
import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';
import AdditionalItemPage from './pages/AdditionalItemPage';
import ReturnOfVehiclePage from './pages/ReturnOfVehiclePage';
import PolicyPage from './pages/PolicyPage';
import CarPage from './pages/CarPage';
import CarListPage from './pages/CarsListPage';
import NavBar from './NavBar';
import Footer from './Footer';
import NotFoundPage from './pages/NotFoundPage';
import { PrivateRoute } from './auth/PrivateRoute';
function App() {
return (
<BrowserRouter>
<div className="App">
<NavBar />
<div>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/signup" element={<SignUpPage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/cars" element={<CarListPage />} />
<Route path="/cars/:carId" element={<CarPage />} />
<Route path="/additional-item" element={<AdditionalItemPage />} />
<Route path="/return-vehicle" element={<ReturnOfVehiclePage />} />
<Route path="/policy" element={<PolicyPage />} />
<Route
path="/profile"
element={
<PrivateRoute>
<ProfilePage />
</PrivateRoute>
}
/>
<Route path="*" element={<NotFoundPage />} />
</Routes>
</div>
<Footer />
</div>
</BrowserRouter>
);
}
export default App;
PrivateRoute.js comp
import { Route, Navigate } from 'react-router-dom';
import { useUser } from './useUser';
export const PrivateRoute = (props) => {
const user = useUser();
if (!user) return <Navigate to="/login" />;
return <Route {...props} />;
};
I've already tried to wrap everyting I though of in the
The actual Profile paga into Routes
The PrivteRoute into Routes
The Route into another Routes
Creating another Routes, separeted with the 1st one.
If remove PrivateRoute, everything works fine but it s not the solution I'm looking for.
You just didn't implement it properly , just change your PrivateRoute.js component like this :
import { Navigate } from 'react-router-dom';
import { useUser } from './useUser';
function PrivateRoute({ children }) {
const user = useUser();
return user ? children : <Navigate to="/login" />;
}
The error message is accurate in that a Route must be directly placed within a Routes wrap. To get around this, simply create a 2nd top-level Routes wrap and move the Route logic out of your PrivateRoute.tsx file.
Consider this example sandbox I created with some slight modifications to your code. You can switch user to false/true to see how the /profile private route reacts as expected.
Here's the bare-bones implementation:
App.tsx
import { BrowserRouter, Route, Routes } from "react-router-dom";
import PrivateRoutes from "./PrivateRoutes";
function App() {
return (
<BrowserRouter>
<div className="App">
<div>
<Routes>
<Route path="/" element={<div>Hello World</div>} />
</Routes>
<PrivateRoutes /> {/* <---- all private routes ---- */}
</div>
</div>
</BrowserRouter>
);
}
export default App;
PrivateRoutes.tsx
import { Route, Routes } from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
export default function PrivateRoutes() {
return (
<Routes>
<Route
path="/profile"
element={
<PrivateRoute>
<div>Private Profile</div>
</PrivateRoute>
}
/>
</Routes>
);
}
PrivateRoute.tsx
import { ReactNode } from "react";
import { Navigate } from "react-router-dom";
interface PropsI {
children: ReactNode;
}
export default function PrivateRoute({ children }: PropsI) {
const user = false;
if (!user) return <Navigate to="/fail" />;
return <>{children}</>;
}

page does not redirect after setting token with localstorage

I am following the tutorial below to create a token system for my login application.
https://www.digitalocean.com/community/tutorials/how-to-add-login-authentication-to-react-applications
Here is my code:
App.js:
import logo from './logo.svg';
import './App.css';
import "bootstrap/dist/css/bootstrap.min.css";
import useToken from './components/useToken';
import Navbar from './components/Navbar';
import AudioDropzone from './components/AudioDropzone';
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useLocation
} from "react-router-dom";
import Upload from './pages/Upload';
import Manage from './pages/Manage';
import Menu from './pages/Menu';
import Admin from './pages/Admin';
import Login from './pages/Login';
import { useEffect, useState } from 'react';
<script src="https://unpkg.com/boxicons#latest/dist/boxicons.js"></script>
function App() {
const { token, setToken } = useToken();
console.log(token)
if(!token) {
return(
<div className="App">
<div className="row">
<div class="col-2">
</div>
<div class="col-9">
<Login setToken={setToken} />
</div>
</div>
</div>
);
}
return (
<div className="App">
<Router>
<head>
<link rel="stylesheet" href="boxicons.min.css" />
</head>
<div className="row" >
<div class="col-2">
</div>
<div class="col-9">
<Switch>
<Route path="/upload">
<Upload customer_id={token}/>
</Route>
<Route path="/manage">
<Manage customer_id={token} />
</Route>
<Route path="/menu">
<Menu customer_id={token} />
</Route>
<Route path="/admin">
<Admin />
</Route>
<Route path="/admin/upload">
<Upload />
</Route>
<Route path="/admin/manage">
<Manage />
</Route>
<Route path="/admin/menu">
<Menu />
</Route>
</Switch>
</div>
</div>
</Router>
</div>
);
}
export default App;
useToken.js:
import { useState } from 'react';
export default function useToken() {
const getToken = () => {
const tokenString = localStorage.getItem('token');
const userToken = JSON.parse(tokenString);
return userToken?.token
};
const [token, setToken] = useState(getToken());
const saveToken = userToken => {
localStorage.setItem('token', JSON.stringify(userToken));
setToken(userToken.token);
};
return {
setToken: saveToken,
token
}
}
Currently, when login is successful, it just clears the fields and shows the same login page again without showing the second version of App with all of my content. The issue is that the token value is not being updated and populated. I have verified that upon logging in token is updated to an integer value like 13.
This is seen when I log the value of userToken in useToken.js:
const saveToken = userToken => {
console.log(userToken) //returns 13
localStorage.setItem('token', JSON.stringify(userToken));
setToken(userToken.token);
};
Let me know what I am doing wrong!
I believe the bug is in the saveToken function.
setToken(userToken.token); should be setToken(userToken);
You don't need the .token because userToken is not an object. It is just an integer value.

How to redirect page after login in react.js

I am new to reactjs and trying to redirect the page after a user registers. Here is my App.js, which has a register input.
import React, { useState } from "react";
import './App.css';
import axios from 'axios';
function App() {
const [registerUsername, setRegisterUsername] = useState("");
const [registerPassword, setRegisterPassword] = useState("");
const [registerPasswordAgain, setRegisterPasswordAgain] = useState("");
const [registerEmail, setRegisterEmail] = useState("");
const register = () => {
axios({
method: "post",
data: {
registerUsername,
registerPassword,
registerPasswordAgain,
registerEmail
},
withCredentials: true,
url: "http://localhost:4000/register"
}).then((res) => console.log(res));
}
return (
<div className="App">
<div class = "topnav">
<a class="active" href="/register.html">Register </a>
<a class="active" href="/play.html">Play</a>
</div>
<div>
<h1>Register</h1>
<input placeholder ="Enter your username" onChange={e => setRegisterUsername(e.target.value)} />
<input type="password" placeholder ="Enter your password" onChange={e => setRegisterPassword(e.target.value)}/>
<input type="password" placeholder ="Enter your password again" onChange={e => setRegisterPasswordAgain(e.target.value)} />
<input placeholder ="Enter your email" onChange={e => setRegisterEmail(e.target.value)} />
<button onClick={register}>Submit</button>
</div>
</div>
);
}
this sends the registration username/password to my backend which is written in node.js. Assuming I send back a 200 OK status, or something that indicates that the user has registered, how can I redirect this page to another, lets say a login page? Do I need to create a class that extends component?
Just wondering where to go from here.
Thanks
use React-Router-dom
and in react we don't use anchor tag because react is SPA (Single page application), so use LINK tag
simple Example
index.js
import {BrowserRouter} from 'react-route-dom'
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
app.js
import {Switch,Rout,Link} from 'React-router-dom'
<div>
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/users"> {/add your redirect path/}
<Users /> {/Users components will render after the URL changes }
</Route>
<Route path="/" exact>
<Home />
</Route>
</Switch>
<Link to="/user">
<button>
Login
</button>
</Link>
</div>
using react-router-dome Redirect component you can redirect your page to abother page.
in your case you can store a boolean state and make it true when user suucessfully logged in.
import { Redirect } from "react-router-dom";
....
const [logged,setLogged] = useState(false)
.....
url: "http://localhost:4000/register"
}).then((res) => if(res.Sucess){
setLogged(true)
};
in your main component. you can add a redirect component like.
<div className="App">
{logged && <Redirect to="redirect-url" />}

Not Able to render new Component on link change

Actually when ever a person click's on Sign in Button the link change but the component on that page does not renders
App.js
import { BrowserRouter as Switch, Route } from 'react-router-dom';
const App = () => (
<Switch>
<Route path="/" exact component={Join} />
<Route path="/call" exact component={Call} />
</Switch>
);
export default App;
Join.js
return (
<div className="container">
<div><input id="room" placeholder="room name" onChange={(event)=>setRoom(event.target.value)}></input></div>
<BrowserRouter>
<Link onClick={event => (!room) ? event.preventDefault() : null} to={`/call?room=${room}`}>
<button className="button mt-20" type="submit" onClick={run}>Sign In</button>
</Link>
</BrowserRouter>
</div>
);
}
export default Join;
Call.js
const Call = () => {
return (
<div>
<h1>jhjcjc</h1>
</div>
);
}
export default Call;
Please look over this error many developer's are facing this problem.
In your Join component, there is no need to wrap Link in a BrowserRouter. Check out the working demo here.

React-Router how to prevent from redirect

So I have a problem in my code that when I refresh my page it sends me a NotFound page, but when I don't refresh the page still works while changing the route (single page application)
My routes code:
import React, { Component } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Navigation from './src/containers/Navigation/Navigation';
import PageProjects from './src/containers/PageProjects/PageProjects';
import NewPost from './src/components/pages/Projects/NewPost/NewPost'
import Home from './src/components/pages/Home/Home';
import Calendar from './src/components/pages/Calendar/Calendar';
import Team from './src/components/pages/Team/Team';
import Settings from './src/components/pages/Settings/Settings';
import NotFound from './src/components/pages/NotFound/NotFound';
class App extends Component {
render() {
return (
<div className="App" style={{ height: '100%' }}>
<BrowserRouter>
<div>
<Navigation />
<Switch>
<Route path="/profile" component={Home} exact />
<Route path="/projects" component={PageProjects} exact />
<Route path="/calendar" component={Calendar} exact />
<Route path="/team" component={Team} exact />
<Route path="/newpost" component={NewPost} exact />
<Route path="/settings" component={Settings} exact />
<Route path="*" component={NotFound} exact />
</Switch>
</div>
</BrowserRouter>
</div>
);
}
}
export default App;
Only my "/profile" page works because it's the page that I'm sending the index.html file from my route server
app.get('/profile', function(req, res){
res.sendFile(__basedir + "/react/index.html");
});
My Navigation Component:
import React, { Component } from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import Toolbar from '../../components/Toolbar/Toolbar';
import SideBar from '../../components/SideBar/SideBar';
import Backdrop from '../../components/Backdrop/Backdrop';
import Home from '../../components/pages/Home/Home';
import Calendar from '../../components/pages/Calendar/Calendar';
import Team from '../../components/pages/Team/Team';
import Settings from '../../components/pages/Settings/Settings';
import NotFound from '../../components/pages/NotFound/NotFound';
class Navigation extends Component {
state = {
sideBarOpen: false,
}
barToggleClickHandler = () => {
this.setState((prevState) => {
return{sideBarOpen: !prevState.sideBarOpen};
});
};
backdropClickHandler = () => {
this.setState({sideBarOpen: false});
};
render() {
let backdrop;
if(this.state.sideBarOpen){
backdrop = <Backdrop click={this.backdropClickHandler}/>;
}
return (
<section className="Navigation">
<Toolbar barClickHandler={this.barToggleClickHandler} />
<SideBar show={this.state.sideBarOpen}/>
{backdrop}
<main style={{marginTop: '150px'}}>
</main>
</section>
);
}
}
export default Navigation;
What can I do?
Thanks
(I'm using Reactjs, Nodejs, Express..)
app.get('/', function(req, res){
res.sendFile(__basedir + "/react/index.html");
});
or use hashrouter
<HashRouter>
<div>
<Navigation />
<Switch>
<Route path="/profile" component={Home} exact />
<Route path="/projects" component={PageProjects} exact />
<Route path="/calendar" component={Calendar} exact />
<Route path="/team" component={Team} exact />
<Route path="/newpost" component={NewPost} exact />
<Route path="/settings" component={Settings} exact />
<Route path="*" component={NotFound} exact />
</Switch>
</div>
</HashRouter>
This should do the trick !
app.get('/', function(req, res){
res.sendFile(__basedir + "/react/index.html");
});

Resources