I'm upgrading react router to react router v6. In the newest version withRouter is not supported anymore. I implemented this wrapper as a replacement.
export const withRouter = Component => {
const Wrapper = props => {
const navigate = useNavigate();
return <Component navigate={navigate} {...props} />;
};
return Wrapper;
};
I implemented it like that
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps
)(Layout)
);
basically the wrapper is at the same spot as the original withRouter was in the application.
Currently I get the error
Uncaught Error: [Wrapper] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
I exchanged <Component navigate={navigate} {...props}/> with <Route navigate={navigate} {...props}/>, however I still get the same error. Thanks!
Wrapper is the returned component that you appear to be trying to render as a route component in a Routes component, but as the error points out, this is an invariant violation.
Given:
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps
)(Layout)
);
It seems you are trying to directly render this component in the Routes component, something like:
<Routes>
<DecoratedLayout> // <-- Not a Route component!!
<Route ....... />
...
</DecoratedLayout>
</Routes>
This is invalid though. In this example the DecoratedLayout component should be rendered as a Wrapper component (renders children prop) or on a Layout Route (renders Outlet component).
As a Wrapper component:
<Routes>
<Route
path="...."
element={(
<DecoratedLayout>
...
</DecoratedLayout>
)}
/>
...
</Routes>
As a Layout Route:
<Routes>
<Route element={<DecoratedLayout />}>
<Route ....... />
...
</Route>
...
</Routes>
Related
I need to do something like
const RoutesList = () => (
<Fragment>
<Route .../>
<Route .../>
<Route .../>
</Fragment>
)
and use this inside Routes
...
<Routes>
<RoutesList/>
</Routes>
...
but it throws error saying Uncaught Error: [PreBuiltRoutes] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
Even though i am returning Fragment.
Workaround (Edit)
Actually, you can execute RoutesList as a function, but do not use hooks within.
<Routes>{RoutesList()}</Routes>
The problem is that <RoutesList> is considered by react-router as any other component, but it expect an array of <Route> or <React.Fragment>.
I suggest 2 methods at the moment:
Exporting an Array
Using react-router-dom v6, you can do this in Routes.js
import React from "react";
import { Route } from "react-router-dom";
export default [
<Route .../>,
<Route .../>,
<Route .../>
];
then, use it like this:
import Routes from "./Routes"
...
<Routes>...[{routes}]</Routes>
...
Exporting React.Fragment directly
Also, you can export a fragment:
export const RoutesAsFragment = (
<>
<Route ...>
<Route ...>
<Route ...>
</>
);
and use them as an object:
<Routes>{RoutesAsFragment}</Routes>
here is an example
Following this tutorial series to try to build a simple React, Electron, and firebase app.
This project mirrors this demo project. I'm getting a lot of compile errors, mostly outdated content and dependencies, but managed to fix most of them up. The main thing I'm struggling with now is upgrading some code from react-router v5 to v6, specifically in app.js
import React, { useState, useEffect } from "react";
import { Router, Routes, Route, Navigate } from "react-router-dom";
import AddMoviePage from "../pages/add-movie-page";
import EditMoviePage from "../pages/edit-movie-page";
import AccountPage from "../pages/account-page";
import MoviesPage from "../pages/movies-page";
import NotFoundPage from "../pages/not-found-page";
import { auth } from "../data/firebase";
import Nav from "./nav";
import { createMemoryHistory } from "history";
function AuthenticatedRoute(props) {
const { isAuthenticated, children, ...routeProps } = props;
return <Route {...routeProps}>{isAuthenticated ? children : <Navigate to="/account" />}</Route>;
}
function App() {
const [user, setUser] = useState(null);
const isAuthenticated = user !== null;
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((currentUser) => {
setUser(currentUser);
});
return unsubscribe;
}, []);
const history = createMemoryHistory();
console.log(history);
return (
<Router history={history}>
<Nav user={user} />
<Routes>
<Route path="/account">
<AccountPage user={user} />
</Route>
<AuthenticatedRoute path="/" exact isAuthenticated={isAuthenticated}>
<MoviesPage user={user} />
</AuthenticatedRoute>
<AuthenticatedRoute path="/add" isAuthenticated={isAuthenticated}>
<AddMoviePage user={user} />
</AuthenticatedRoute>
<AuthenticatedRoute path="/edit/:id" isAuthenticated={isAuthenticated}>
<EditMoviePage user={user} />
</AuthenticatedRoute>
<Route path="*">
<NotFoundPage />
</Route>
</Routes>
</Router>
);
}
export default App;
I'm getting the following error and can't really figure out what's going on:
Uncaught TypeError: Cannot read properties of undefined (reading 'pathname')
The above error occurred in the <Router> component.
Issues
The main issue here is that you are importing and using the low-level Router component instead of one of the high-level routers (i.e. BrowserRouter, MemoryRouter, HashRouter, etc). The Router component has a couple required props and history isn't one of them.
Router Interface:
declare function Router(
props: RouterProps
): React.ReactElement | null;
interface RouterProps {
basename?: string;
children?: React.ReactNode;
location: Partial<Location> | string; // <-- required!
navigationType?: NavigationType;
navigator: Navigator; // <-- required!
static?: boolean;
}
The high-level routers all instantiate/manage a history reference internally and pass the required props and render the base Router.
Additional issues found in the code:
Another issue is that in react-router-dom#6 custom route components are no longer valid. Only Route components can be rendered by the Routes component. You'll instead convert your older v5 custom route components, a.k.a. AuthenticatedRoute, either into Wrapper components that render the children prop, or as the preferred method a Layout Route.
A final related issue is that Route components and only be rendered by the Routes component or other Route components in the case of building nested routes. In other words, the only valid children components of a Route component is another Route component. The routed content you want to be rendered on a route is passed to the Route component's element prop.
Solution
Convert AuthenticatedRoute to a layout route.
import { Navigate, Outlet } from 'react-router-dom';
function AuthenticatedRoute({ isAuthenticated }) {
if (isAuthenticated === undefined) {
// Don't render the protected content or redirect until we confirm
// authentication status.
return null; // or loading indicator/spinner/etc
}
return isAuthenticated ? <Outlet /> : <Navigate to="/account" replace />;
}
It seems you are wanting to really use a MemoryRouter since you are instantiating your own MemoryHistory object. Import and render the MemoryRouter directly. Move the route "children" onto their respective route's element prop.
Example:
...
import {
MemoryRouter as Router, // <-- import high-level router
Routes,
Route,
Navigate,
Outlet
} from "react-router-dom";
...
function AuthenticatedRoute({ isAuthenticated }) {
if (isAuthenticated === undefined) {
return null; // or loading indicator/spinner/etc
}
return isAuthenticated ? <Outlet /> : <Navigate to="/account" replace />;
}
function App() {
const [user, setUser] = useState(); // <-- initially not auth'd or unauth'd
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((currentUser) => {
setUser(currentUser); // <-- sets to user object or null
});
return unsubscribe;
}, []);
return (
<Router> // <-- Now really a MemoryRouter
<Nav user={user} />
<Routes>
<Route path="/account" element={<AccountPage user={user} />} />
<Route element={<AuthenticatedRoute isAuthenticated={user} />}>
<Route path="/" element={<MoviesPage user={user} />} />
<Route path="/add" element={<AddMoviePage user={user} />} />
<Route path="/edit/:id" element={<EditMoviePage user={user} />} />
</Route>
<Route path="*" element={<NotFoundPage />} />
</Routes>
</Router>
);
}
export default App;
I was applying authenication in my project in React.js by using protectes routes. First i was using Redirect component from react-router-dom but then i have found out the changes they made in react-router-dom than i applied the navigate component.
import {BrowserRouter,Routes,Route,Navigate} from 'react-router-dom';
import './App.css';
import Navigation from './components/shared/Navigation/Navigation';
import Authenticate from './pages/Authenticate/Authenticate';
import Home from './pages/Home/Home';
import Login from './pages/Login/Login';
import Register from './pages/Register/Register';
const isAuth = true;
function App() {
return (
<div className="App">
<BrowserRouter>
<Navigation/>
<Routes>
<Route exact path='/' element={<Home/>}></Route>
{/* <Route exact path='/register' element={<Register/>}></Route>
<Route exact path='/login' element={<Login/>}></Route> */}
{/* <Route exact path='/authenticate' element={<Authenticate/>}></Route> */}
<GuestRoute exact path='/authenticate' element={<Authenticate/>}></GuestRoute>
</Routes>
</BrowserRouter>
</div>
);
}
const GuestRoute = ({children,...rest}) =>{
return(
<Route {...rest} render = {({location})=>{
return isAuth ?(
<Navigate to = '/rooms'state = {{from : location}} replace />
)
:(
children
)
}}></Route>
)
}
export default App;
This is my code after using the navigate component my screen not showing any thing there must be some kind of logical error in it. Kindly help me to resolve this error.
your app is showing nothing because the route /rooms does not match any routes in the <Routes /> component
I followed the Auth0 React Authentication guide written here:
https://auth0.com/blog/complete-guide-to-react-user-authentication
And implemented the ProtectedRoute component as outlined in the tutorial:
import React from "react";
import { Route } from "react-router-dom";
import { withAuthenticationRequired } from "#auth0/auth0-react";
import { Loading } from "../components/index";
const ProtectedRoute = ({ component, ...args }) => (
<Route
component={withAuthenticationRequired(component, {
onRedirecting: () => <Loading />,
})}
{...args}
/>
);
export default ProtectedRoute;
But now I am having an issue with the ProtectedRoute component that doesn't exist if I use withAuthenticationRequired directly in the export statement of the component that I am trying to protect. I have a web app that contains routes like the following:
<Router>
{isAuthenticated && <Header />}
<Switch>
<Route exact path='/'>
{isAuthenticated ? <Redirect to="/home" /> : <LandingPage />}
</Route>
<ProtectedRoute path='/home' component={Home}/>
<ProtectedRoute path='/events' component={Events}/>
<ProtectedRoute path='/dates' component={Dates}/>
</Switch>
</Router>
And my Home component contains something like the following:
function Home(){
return <div className="home-page">
<Sidebar />
<ProtectedRoute path={"/home/dogs"} component={Dogs}/>
<ProtectedRoute path={"/home/cats"} component={Cats}/>
</div>
}
export default Home;
The bug also happens when the Home component doesn't use ProtectedRoute like so:
function Home(){
return <div className="home-page">
<Sidebar />
<Route path={"/home/dogs"} component={Dogs}/>
<Route path={"/home/cats"} component={Cats}/>
</div>
}
export default Home;
I can't explain why it happens, but it prevents the state within the Sidebar component from changing the sidebar's appearance and rendered components.
Here is a link to a codesandbox on how the sidebar should work (no auth0).
https://codesandbox.io/s/react-routing-problem-2efic
When using ProtectedRoute as in the code above, the active class on the navbar links changes, but the rest of the content stays the same.
However, if I instead take the ProtectedRoute off of the Home component, but use withAuthenticationRequired on the export of the Home component, like so:
export default withAuthenticationRequired(Home, {
onRedirecting: () => (<div>Redirecting you to the login page...</div>)
});
and
<Route path='/home' component={Home}/> //instead of ProtectedRoute
Then everything works as it should.
My questions are:
Why is the ProtectedRoute component behaving differently from when withAuthenticationRequired is at the export level?
Do I need to protect routes that are nested within a protected route?
Thanks for any help!
I'm trying to figure out how to pass React Router's location prop to a component.
I have a route defined like so:
<Route
path="placeholder"
render={(props) => <Navigation {...props} />}
/>
In my Navigation component I do console.log(this.props); in the render method only to get an empty object. What gives? Isn't the location prop supposed to be automatically supplied to any component inside a Route?
By the way, I'm using react-router-dom version 4.2.2.
You need to wrap your component with withRouter in order to inject match, history and location in your component props.
import { withRouter } from 'react-router-dom';
class Navigation extends React.Component {
render() {
const { match, location, history } = this.props
return (
<div>{location.pathname}</div>
)
}
}
export default withRouter(Navigation)
You need to use the withRouter function from 'react-router-dom' on you main component where you setup Routes wrapped in a Switch and you should have access to location prop on Navigation Component by using this.props.location
App.js
Class App extends Component {
render(){
return (
<Aux>
<Switch>
<Route path="/login" exact component={Login}/>
<Route path="/Navigation" component={Navigation}/>
<Redirect to="/login"/>
</Switch>
</Aux>
);
}
}
export default withRouter(App);