Im new to this website so bear with me.
I'm developing an appointment based website in which I have a login page, a profile page, and a register page. Im trying to implement a programatic redirect to a users profile, I opted to use useNavigation from react-router-dom. I attempted to use the navigate function like the documentation says but it crashes my website after the render. I stepped through my code with my debugger and the correct page does render, however, somewhere after the render, my site goes blank and returns an error im confused by. I've managed to simplify/obfuscate my code aswell in the examples below.
This is the applicable code to my problem. When the page is rendered it will check for a JWT in local storage, it then verifies the JWT in my server and returns data, i expected that if the jwt is verified then the page will redirect to my profile page route. which it does do, but then goes blank.
The profilePage route is currently unprotected for simplicity so i can figure out why useNavigate isn't working.
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
app.js
import Appbar from './appbar.jsx';
import { Route, Routes } from 'react-router-dom';
import Login from "./components/login/login.jsx";
import ProfilePage from "./components/profilePage/profilePage.jsx";
export default function App() {
return (
<div className="App">
<Appbar/>
<>
<Routes>
<Route path="/" element={<Home /> }/>
<Route path="/login"/> element={<Login />}/>
<Route path="/profilePage" element={<ProfilePage />}/>
</Routes>
</>
</div>
);
}
login.jsx
import { useNavigate } from 'react-router-dom';
import { useEffect } from 'react';
export default function Login(props) {
const navigate = useNavigate()
/*
EDIT: I added this here because this was in my original code that got me my original error
*/
useEffect( async () => {
// My express route verifying the JWT
const response = await fetch('/users/userAuth', {
method: 'POST',
headers: {
'x-access-token': localStorage.getItem('token'),
},
})
const data = await response.json()
if (data.isLoggedIn === true)
{
navigate("/profilePage")
}
else {
console.log("not logged in")
}
}, [])
profilePage.jsx
export default function ProfilePage() {
// Keeping it simple until i can get useNavigate to work
return (
<div>
<h1>Profile Page</h1>
</div>
)
}
This is the error I get:
React Error from useNavigate
EDIT: Thanks for the patience regarding the edits. In an effort to obfuscate I removed the crucial parts of my code. Although im still getting the same error
in your code I think you forgot to define navigate
const navigate = useNavigate();
I think that the problem is on the async function passed in React.useEffect. You should create the async function inside React.useEffect's setup and then call it.
Have a look here on how to use async functions with React.useEffect
Related
I am using google analytics in my react project and it doesn't show any active users even when I am online. I have tried different approaches that I found online but none seem to work. I have only tried it on localhost and not on a deployed website but I think it should still work.
Here is my code.
My app.js
import React, { Suspense, useEffect } from "react";
import "./App.css";
import IntroPage from "./containers/IntroPage/IntroPage";
import Layout from "./containers/Layout/Layout";
import { Switch, Route, Redirect, withRouter } from "react-router-dom";
import Spinner from "./Components/UI/Spinner/Spinner";
import ReactGA from "react-ga";
const Contact = React.lazy(() => import("./Components/Contact/Contact"));
const trackingId = "UA-171033255-1";
ReactGA.initialize(trackingId);
function App() {
useEffect(() => {
ReactGA.pageview(window.location.pathname + window.location.search);
}, []);
return (
<div className="App">
<Layout>
<Switch>
<Route
path="/contact"
render={() => (
<Suspense
fallback={
<div className="Fallback">
<Spinner />
</div>
}
>
<Contact />
</Suspense>
)}
/>
<Route path="/" component={IntroPage} />
<Redirect to="/" />
</Switch>
</Layout>
</div>
);
}
export default withRouter(App);
What am I doing wrong here?
Updating from react-ga to react-ga4 worked for me.
Change the import to react-ga4 and instead of ReactGA.pageview(...) use ReactGA.send("pageview").
Have you tried adding the tracking code inside <head> in index.html (Admin -> Property -> Tracking code)?
Since you are using empty dependencies array in useEffect your code gets executed only once, when <App/> rendered. So you are not sending pageviews on route change.
You need to put pageview-related code before your App component, right after ReactGA initialization. It's a usage example from docs.
I had a similar issue try disabling your ad block if you have it active and put the ReactGA.initialize inside the useEffect
import ReactGA from "react-ga4";
ReactGA.initialize("your measurement id");
ReactGA.send("pageview");
I have a header that appears in 95% of pages in my site, so I mount it in my main App component. For the other 5% of pages though I need it to be gone.
I figured a good way to do it would be to change a state from true to false based on the current route, and that state would determine whether the header mounts or not.
at first I tried just using window.location.href as a useEffect dependency but that didn't seem to work. I've read about the option to listen to the location of the history, but I keep getting Cannot read property 'history' of undefined. I thing that perhaps it's because I am using a custom history component, or maybe because I try to do so in my main App component? Not sure.
This is my history object:
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
I use it in my Router like this:
<Router history={history}>
CONDITIONAL COMPONENT
<Switch>
...routes
</Switch>
</Router>
This is how I try to listen, where history is my custom history object
const MyHistory = useHistory(history)
useEffect(() => {
return MyHistory.listen((location) => {
console.log(`You changed the page to: ${location.pathname}`)
})
},[MyHistory])
You are trying to use useHistory within a component that renders Router which is a Provider. In order for useHistory to Work it needs to have a Router higher up in the hierarchy.
So either you wrap the App component with Router like
export default () => (
<Router history={history}><App /><Router>
)
or since you define a custom history you can use it directly without using useHistory
useEffect(() => {
return history.listen((location) => {
console.log(`You changed the page to: ${location.pathname}`)
})
},[])
This is how I control every route change in my App. I created a component that listen to the pathname property given by useLocation
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function AppScrollTop() {
const { pathname } = useLocation();
useEffect(() => {
console.log(`You changed the page to: ${pathname}`)
}, [pathname]);
return null;
}
Then I put the component inside the <Router>
<BrowserRouter>
<AppScrollTop />
<Switch>
<Route path="/login" component={Login} />
</Switch>
</BrowserRouter>
I hope this helps.
I am working on a React app that is 'remote controlled' and am trying to connect the app's tab navigation via Pusher. I am using react-router-dom and have set up my connection to Pusher and the channels it listens to in the componentDidMount. I need to have the app change URL navigation each time a message is returned but cannot figure out what the correct way to 'push' it is. I have google many threads about programmatically navigating react-router-dom and have tried this.history.push(), this.props.history.push() and it's always throwing an error that history is undefined. The listeners reside in the same Component as the routing itself.
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import axios from 'axios';
import Pusher from 'pusher-js';
import Component1 from './Component1';
import Component2 from './Component2';
export default class AppRouter extends Component{
componentDidMount() {
const pusher = new Pusher('8675309', { cluster: 'us2', encrypted: true });
const nav_channel = pusher.subscribe('nav');
nav_channel.bind('message', data => { this.props.history.push(data.message) });
}
...
render(){
return(
<Router>
<Switch>
<Route path='/path1' render={() => <Component1 navigate={this.handleNavChange} />} />
<Route path="/path2" render={() => <Component2 navigate={this.handleNavChange} />} />
</Switch>
</Router>
)
}
}
I need the app to change the URL or the routing each time the message is received from another connected app but I cannot figure out how to make react-router-dom (v4) do it within the same component as the Router itself. Any information or pointing me to a resource would be highly appreciated.
You need to use the withRouter decorator from React-Router to get access to match, location, and history.
import { withRouter } from 'react-router'
Wrap your component when exporting
export default withRouter(yourComponent)
Then you will have access to history from props.
https://reacttraining.com/react-router/core/api/withRouter
To add on to andrewgi's answer:
After using withRouter() on your component, OR a parent component,
try the following code in your component:
static contextTypes = {
router: PropTypes.object,
location: PropTypes.object
}
These should be defined, you can try console.log(this.context.router, this.context.location)
Then, to navigate programmatically, call
this.context.router.history.push('/someRoute');
Note: the context API is not stable. See Programmatically navigate using react router and https://reactjs.org/docs/context.html#why-not-to-use-context.
I've implemented an App component which contains a Route using React and React router:
import {
BrowserRouter as Router,
Route
} from 'react-router-dom';
import createHeader from './components/header/header';
import createLandingPage from './components/landing-page/landing-page';
import createFooter from './components/footer/footer';
export default React => () => {
const Header = createHeader(React);
const LandingPage = createLandingPage(React);
const Footer = createFooter(React);
return (
<section id="sectionId">
<Header />
<Router>
<Route exact path="/" component={LandingPage} />
</Router>
<Footer />
</section>
);
};
The app itself renders and works properly, as I can go to my browser and see everything as expected.
The problem arises when I try to implement proper testing, which runs in Node.js without a browser:
import test from 'tape';
import dom from 'cheerio';
import React from 'react';
import reactDom from 'react-dom/server';
import { MemoryRouter } from 'react-router';
import createApp from './app';
const render = reactDom.renderToStaticMarkup;
test('App init & render', assert => {
const message = 'It sohuld initialize and render the app.';
const App = createApp(React);
const $ = dom.load(render(
<MemoryRouter>
<App />
</MemoryRouter>
));
const output = $('#jptv').length;
const actual = output > 0;
const expected = true;
assert.equal(actual, expected, message);
assert.end();
});
I used MemoryRouter to wrap my app as indicated in React docs to avoid errors, but with or without it, I have the same error when running the test in the console:
Invariant Violation: Browser history needs a DOM
My guess is that I cannot run a component using BrowserRouter out of the browser, yet I don't understand what would be the proper way to test it, as I don't want to force myself to manipulate the code as to fit the test environment shortcomings.
I've searched the web and look into similar issues, but I cannot find an answer, and the docs offer little if no useful information to deal with this.
Any help will be most appreciated.
Thanks!
Currently in my root component, it is set to go directly to the Login page as default. But I would like to set it up where it checks to see if a token already exists in the local storage, and if it does, skip the Login page and navigate directly to the Home page.
With the following code I have set up, it navigates to the Home page but for a split second the Login page appears before navigating to the Home page.
How can I go directly to the Home page without the Home page showing up at all?
Here is the code:
import React from 'react';
import {render} from 'react-dom';
import configureStore from '../redux/store'
import { Provider } from 'react-redux'
import { Router, Route, IndexRoute, hashHistory } from 'react-router'
import cookie from 'react-cookie';
import actions from '../redux/actions'
import Login from '../components/Login'
import App from '../components/App'
import Home from '../components/Home'
let store = configureStore(initialState)
//Takes the token from local storage and set to const token
const token = cookie.load('token');
//This checks to see if the token exists in the local storage, and if it does, it enters the if statement and goes directly to the Home component.
if(token) {
window.location.href = 'http://localhost:3000/#/App'
}
render(
<div>
<Provider store={store}>
<Router history={hashHistory}>
<Route
component={Login}
path='/'
/>
<Route
component={App}
path='App'
>
<IndexRoute
component={Home}
/>
</Route>
</Router>
</Provider>
</div>,
document.getElementById('app')
)
I would create a root component called LoginCheck, in this component you would use the componentWillMount lifecycle hook to determine whether a user is logged in or not. If the user is logged in, it would continue to the correct page, otherwise it would redirect to the login page.
Hope this helps.
Hope it helps.
import {browserHistory} from 'react-router';
export class Home extends React.Component{
constructor(props, context)
{
super(props, context);
}
componentWillMount() {
if(token)
{
browserHistory.push("YOUR AUTHENTICATED PAGE");
}
}
}
you can write something like this in component will mount
import { push } from 'react-router-redux';
//login component
componentWillMount(){
if(token){
this.props.dispatch(push("/home"));
}
}
Explanation from Documentation React-Router-Redux
What if I want to issue navigation events via Redux actions?
React Router provides singleton versions of history (browserHistory
and hashHistory) that you can import and use from anywhere in your
application. However, if you prefer Redux style actions, the library
also provides a set of action creators and a middleware to capture
them and redirect them to your history instance.
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { routerMiddleware, push } from 'react-router-redux'
// Apply the middleware to the store const middleware =
routerMiddleware(browserHistory) const store = createStore(
reducers, applyMiddleware(middleware) )
// Dispatch from anywhere like normal. store.dispatch(push('/foo'))