onEnter not called in React-Router - javascript

Ok, I'm fed up trying.
The onEnter method doesn't work. Any idea why is that?
// Authentication "before" filter
function requireAuth(nextState, replace){
console.log("called"); // => Is not triggered at all
if (!isLoggedIn()) {
replace({
pathname: '/front'
})
}
}
// Render the app
render(
<Provider store={store}>
<Router history={history}>
<App>
<Switch>
<Route path="/front" component={Front} />
<Route path="/home" component={Home} onEnter={requireAuth} />
<Route exact path="/" component={Home} onEnter={requireAuth} />
<Route path="*" component={NoMatch} />
</Switch>
</App>
</Router>
</Provider>,
document.getElementById("lf-app")
Edit:
The method is executed when I call onEnter={requireAuth()}, but obviously that is not the purpose, and I also won't get the desired parameters.

onEnter no longer exists on react-router-4. You should use <Route render={ ... } /> to get your desired functionality. I believe Redirect example has your specific scenario. I modified it below to match yours.
<Route exact path="/home" render={() => (
isLoggedIn() ? (
<Redirect to="/front"/>
) : (
<Home />
)
)}/>

From react-router-v4 onEnter, onUpdate, and onLeave is removed,
according the documentation on migrating from v2/v3 to v4:
on* properties
React Router v3 provides onEnter, onUpdate, and onLeave
methods. These were essentially recreating React's lifecycle methods.
With v4, you should use the lifecycle methods of the component
rendered by a <Route>. Instead of onEnter, you would use
componentDidMount or componentWillMount. Where you would use onUpdate,
you can use componentDidUpdate or componentWillUpdate (or possibly
componentWillReceiveProps). onLeave can be replaced with
componentWillUnmount.

For react-router-6 the Redirect component has been replaced with Navigate.
Below userState will be either null or populated depending upon if user is logged in or not, can be your Redux state for example.
<Route path="/protectedRoute"
element={userState ? <protectedComponent/> : <Navigate to="/login"/>}/>

Related

Why am I getting TypeError: Cannot read property 'push' of undefined react-router-dom

I'm just checking if I have language in redux store or not. If I have, I'll save that in my localStorage, if not I want to navigate to a component call MultiLanguage.
I used below approach using history but it's showing mw an error. What is it showing push of undefined and what could be solution for this?
const history = useHistory();
const language = store.getState().languages.selectedLangauge.language;
console.log("language", language);
if (language) {
localStorage.setItem("language", language);
} else {
history.push("/multilanguage");
}
return (
<div className="font-metropolis_regular">
<Router>
<Route path="/" exact component={Selection} />
<Route path="/login" component={Login} />
<Route path="/home" component={Homepage} />
<Route path="/account" component={Account} />
<Route path="/multilanguage" component={MultiLanguage} />
</Router>
</div>
);
The reason is simple, as the error says, your useHistory hook returns undefined. This happens because this hook can only be called inside a component wrapped by Router so the decision might be moving it one level up.
Take a look at this for code examples https://flaviocopes.com/react-router-uselocation-usehistory-undefined/

React Router: <Redirect push> doesn't update browser url

I'm learning React making a small single page app. Just added react-router-dom today and building it out to do routes and private routes. All is well except for one thing: When the user enters a malformed url in the browser bar, the user should be rerouted to the index (WORKS!), but the browser url bar is not updated on this redirect. Oddly enough, when I hit a private route while not authorized, the redirect DOES update the url bar. What am I missing?
router.js:
const PrivateRoute = ({auth: authenticated, component: Component, ...rest}) => (
<Route {...rest} render={(props) => (
authenticated === true
? <Component {...props} />
: <Redirect to='/login/'/>
)}/>
);
export default function Router() {
const auth = useSelector(isAuthenticated);
return (
<Switch>
<PrivateRoute auth={"auth"} path={"/dashboard/"} component={DashboardContainer}/>
<Route path={"/about/"} component={AboutContainer}/>
<Route path={"/login/"} component={LoginContainer}/>
<Route path={"/terms/"} component={TermsContainer}/>
<Route path={"/"} component={IndexContainer}/>
<Redirect push to={"/"}/>
</Switch>
);
}
I believe your issue is a result of not specifying that the paths should be exact matches, therefore any route will match with your route that is specified as:
<Route path={"/"} component={IndexContainer}/>
Try adding the exact prop to all of your routes (except for your redirect), and you should properly get redirected to the home page with the correct URL.
More details on the exact prop here: React : difference between <Route exact path="/" /> and <Route path="/" />

Multiple path names for a same component in Reach Router

I am using the same component for three different routes:
<Router>
<Home path="/" />
<Home path="/home" />
</Router>
Is there anyway to combine it, to be like:
<Router>
<Home path=["/home", "/"] />
</Router>
For Reach Router: (https://reach.tech/router/example/)
With the exact sample shown, the only way I can see how to do this(on a single line) is with a wildcard.
To find a way to reproduce this without side effects, we would need to see the entire nav menu.
<Router>
<Home path="/*" />
<Chicken path="chicken">
</Router>
...
const Home = props => {
let urlPath = props["*"]
// URL: "/home"
// urlPath === "home"
// URL/: "/"
// urlPath ===""
}
You could continue with other paths below Home and the router would allow them to process.
Check out the the example using a wildcard and reach router on codesandbox, I wrote!
Note: This is a catch-all, but without parsing a parameter is the only single line solution I saw.
Some DrawBacks include Home rendering instead of '404', etc.
//This could be resolved with an if statement in your render
//It will not produce the intended URL either for /home, and I have not looked into that since it is not part of the question.. but if it matched props[*] I'm sure you could redirect or something.
You can read more about the Route Component for Reach Router.
https://reach.tech/router/api/RouteComponent
I wasn't happy with the wildcard solution from the documentation and #cullen-bond because I had to map many other paths and came up with this solution:
<Router>
{["/home", "/", "/other", "/a-lot-more"].map(page => <Home path={page} />)}
</Router>
Example: https://codesandbox.io/s/reach-router-starter-v1-forked-6f44c?file=/src/index.js
Depending on the situation you're dealing with, <Redirect /> could also make the work.
<Router>
<Redirect from="/" path="/home" noThrow />
<Home path="/home" />
</Router>
You can use a single component for mutiple paths, by using a array of routes.
code example :
import sampleComponent from './sampleComponent'; // single component for mutiple routes
<Router>
<Switch>
{["/pathname_1", "/pathname_2", "/pathname_3", "/pathname_4", "/pathname_5", "/pathname_6"].map(pathname => (<Route exact path={pathname} component={sampleComponent} />) )}
<Switch>
<Router>

Private Route getting called dozens of times in React

I keep having maximum update depth exceeded errors and I can't figure out why.
I have the following (pared down, it was more complex originally and actually rendered the component) private route in a private route file:
class PrivateRoute extends Component {
render() {
console.log("private route");
return <Redirect to="/login" />;
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(withKeycloak(PrivateRoute));
And then in my index.js I have the following:
<Route
render={({ location }) => {
const { pathname } = location;
return (
<TransitionGroup>
<CSSTransition
key={pathname}
classNames="page"
timeout={{
enter: 1000,
exit: 1000
}}
>
<Route
location={location}
render={() => (
<Switch>
<Route path="/login" component={LoginPage} />
<Route path="/signup/" component={Signup} />
<PrivateRoute
exact
path="/cards/"
component={Wrapper}
/>
<PrivateRoute
exact
path="/"
component={Wrapper}
/>
...
This should, as far as I can tell, go to the PrivateRoute component for Wrapper on initial load, and then, redirect to the login page, which should not invoke the private route.
Instead, I see:
52 private route
in my console log.
Why am I being redirected back to PrivateRoute dozens of times? Shouldn't this happen once, and that's it?
There's no redirect to anywhere else on the login page at all. There is a login function, but that requires a button click, which is not happening.
Any idea on why this could be happening?
The error message:
in Lifecycle (created by Context.Consumer)
in Redirect (at PrivateRoute.js:11)
in PrivateRoute (created by Context.Consumer)
in WithKeycloak(PrivateRoute) (created by Context.Consumer)
in Connect(WithKeycloak(PrivateRoute)) (at src/index.js:114)
in Switch (at src/index.js:106)
in Route (at src/index.js:103)
in Transition (created by CSSTransition)
in CSSTransition (at src/index.js:95)
in div (created by TransitionGroup)
Originally, the routes looked more like this:
class PrivateRoute extends Component {
render() {
return (
<Route
{...rest}
render={props =>
<Component {...props} />}
/>
)
}
}
rather than
<PrivateRoute
component={Wrapper}
/>
You want to do something like
<Route exact path="/"
render=(props => (<PrivateRoute exact
path="/"
component={Wrapper}>)) />
Otherwise it will just always render your PrivateRoute
Route component is expected to receive a prop exact in order to only render this component when a match exists.
If not exact prop passed, will render it. And then if another match, with render both, and this is why you are getting redirected everytime.
Since you are using a custom component, you must handle this prop to provide it into the Route component.
to fix it, you can follow #TKol approach for example.
<Route exact path="/"
render=(props => (<PrivateRoute
path="/"
component={Wrapper}>))
/>
This way Route is handling that for you and only will render 1 at time.

react-router-dom refresh component when route changes

I used same component for different routes. When route changes, I want the component to be rendered.
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/hotels" component={HotelsPage} />
<Route path="/apartments" component={HotelsPage} />
</Switch>
When I change the route path from /hotels to /apartments, the component HotelsPage doesn't refresh.
What is the cool approach for this?
One of the ways you can get this sorted is by passing the props explicitly like :
<Route path="/hotels" component={props => <HotelsPage {...props} />} />
Firstly you can aggregate the Route into one like
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/(hotels|apartments)" component={HotelsPage} />
</Switch>
and secondly, your HotelsPage component is rendered both on /hotels, /apartments, it is similar case like path params, whereby the component doesn't mount again on path change, but updates thereby calling componentWillReceiveProps lifecycle function,
What you can do is implement componentWillReceiveProps like
componentWillReceiveProps(nextProps) {
if (nextProps.location.pathname !== this.props.location.pathname) {
console.log("here");
//take action here
}
}
DEMO
I guess just passing useLocation().pathname will resolve issue
useEffect(
() => {
// Your logics
});
}, [useLocation().pathname])

Categories