Get React router to cause a re-render - javascript

I have mixed content on my homepage. The user specific content is an edit button next to their own content.
When the user logs out via a logout route this code gets executed:
import React from 'react'; // needed
import ReactDOM from 'react-dom';
import Home from './components/layout/Home.js';
import Login from './Login/Login';
import PollDetails from './components/layout/PollDetails.js';
import EditPoll from './components/presentation/EditPoll.js';
import CreatePoll from './components/presentation/CreatePoll';
import Container from './components/containers/Container.js';
import {Route,Router,browserHistory,IndexRoute} from 'react-router';
import Auth from './utils/Auth';
const mountNode = document.getElementById('root');
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={Container} >
<IndexRoute component={Home} />
<Route path="login" component={Login} />
<Route path="logout" onEnter={(nextState, replace) => {
Auth.deauthenticateUser();
console.log('Logging out src/app.js');
Auth.clearCookie();
// change the current URL to /
replace('/');}} />
<Route path="Polldetailfull/:id" component={PollDetails} />
<Route path="Editthepoll/:id" component={EditPoll} />
<Route path="createPoll" getComponent={(location, callback) => {
if (Auth.isUserAuthenticated()) {
callback(null, CreatePoll);
} else {
callback(null, Home);
}
}} />
</Route>
</Router>,mountNode);
However, the replace('/'); sends you back to the home page but doesn’t re-render any components. Note there is no state to change here. Do I need a state to force the re-render?
Note, if you press refresh on the browser the desired behaviour happens. I tried looking on React Router's code but could not find much about events. To be honest, I don't fully understand onEnter={(nextState, replace) =>

You could use location.reload() to cause it to re-render

onEnter -> this function use when component going to render on browser.
For your idea, You need to use route the component based log details in component life cycle of particular components.below code to be use in path component life cycle(componentWillMount or componentDidMount) and route the page as want .
browserHistory.push('/location-path-name');
call the component based log details in router like below sample
<Route path="createPoll" component={Auth.isUserAuthenticated() ? CreatePoll : Home}/>

Related

How to use Memory Route In React Js ? What is exact method to use route?

I have made use of react js memory router as below in my App.js -
import logo from './logo.svg';
import './App.css';
import './Components/Login'
import Login from './Components/Login';
import {useSelector} from 'react-redux';
import { useEffect } from 'react';
import Welcome from './Components/Welcome';
import{MemoryRouter as Router, Route, Switch} from 'react-router-dom'
function App() {
const state = useSelector(state => state.allReducers)
console.log(state.user.isValid);
return (
<Router>
<Switch>
<Route exact="/" component={Login}></Route>
<Route exact="/" component={Welcome}></Route>
</Switch>
<div className="App">
{state.user.isValid==false ||state.user.isValid== undefined ? <Login></Login> : <Welcome name={state.user.userName}></Welcome>}
</div>
</Router>
);
}
export default App;
But this is displaying my Login component twice on the screen.
How can I avoid this?
Your route configs should be:
<Switch>
<Route path="/login" component={Login} />
<Route path="/" component={Welcome} exact={true} />
</Switch>
exact should be a boolean value. And it'll tell the router only render the route match exactly with the URL. It means, the router only renders the Welcome component when the user stays at /.
But after you changed to my suggestion, you still see 2 login forms if you navigate to /login. Because the div.app will be rendered for every route :D
You're using the same route for both components: <Route exact="/"... so React will show you both. Use different routes for each component. And your syntax is a bit wrong. Like this:
<Switch>
<Route exact path="/"><Welcome /></Route>
<Route exact path="/login"><Login /></Route>
</Switch>

Invalid Hook call when using useState, React

I have little issue I face the first time. I'm trying to use a simple useState but for some reason I can't understand why React throws me back an error and whatever I'm trying to do-nothing fix it.
that's the image of the error:
error description:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
my code:(very simple)
import React, {useState} from "react";
function Login() {
const [user, setUser] = useState({});
return <div> why error? </div>;
}
export default Login;
Tried following their solution with no luck of succeeding... thanks
EDIT: this is where I render the component -
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./components/Home";
import Contact from "./components/Contact";
import Login from "./components/Login";
import Register from "./components/Register";
import NotFound from "./components/NotFound";
import Navbar from "./components/Navbar";
function App() {
return (
<Router>
<div className="App">
<Navbar />
<Switch>
<Route path="/" exact render={Home} />
<Route path="/contact" exact component={Contact} />
<Route path="/login" exact render={Login} />
<Route path="/register" exact render={Register} />
<Route render={NotFound} />
</Switch>
</div>
</Router>
);
}
...
The issue here is how you're rendering your component. You can't do:
Login()
but you can do:
<Login />
Update: similarly, you can't do:
<Route path="/login" exact render={Login} />
you need:
<Route path="/login" exact render={() => <Login />} />
The function itself has no errors. Tried this in another component in a different file. Check for errors elsewhere. What were the other 40 lines in the code?
And what's the version of React?

How to implement links with redirect in next.js?

I have the following react component which I am trying to implement in next.js.
React component:
import React from "react";
import { Route, Switch, Redirect, withRouter } from "react-router-dom";
import Dashboard from "../../pages/dashboard";
import Profile from "../../pages/profile";
function Layout(props) {
return (
<>
<Switch>
<Route
exact
path="/"
render={() => <Redirect to="/app/dashboard" />}
/>
<Route path="/app/dashboard" component={Dashboard} />
<Route path="/app/profile" component={Profile} />
</Switch>
</>
);
}
export default withRouter(Layout);
As I am very new to next. j's, I am not sure on, how can I handle the routes with redirect in next.js similar to above react component code.
Any help is appreciated?
You are using React-Router with NextJS which is possible but not the best practice.
NextJS router is a pretty complicated thing as it handles ClientSide and ServerSide routing simultaneously.
your /app/dashboard and /app/profile pages should render properly. If you want to redirect / to /app/dashboard you can use this trick inside the getInitialProps of the pages/index.js file.

Router.match is not been called on every route change

I have server side rendering app and using react-router for routing. I was using Router.Run Before as the method is no more I am using Router.Match.Previously when there was route change router.run used to be called but same behavior is not happening in the router.match. Is there any reason behind it?
I have defined my router in entry module defined below and it will go to Layout component to look for route paths:
import { Router, Route, IndexRoute, hashHistory } from "react-router";
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={Layout}>
//<IndexRoute component={Featured}></IndexRoute>
<Route path="archives" name="archives" component={Archives}></Route>
<Route path="settings" component={Settings}></Route>
<Route path="featured" component={Featured}></Route>
</Route>
</Router>,
document.getElementById('app'));
Layout component will bind the path defined in router to different components in Layout Component
import { Link } from "react-router";
class Layout extends React.Component(
render(){
return(
{this.props.children}
<li><MenuItem><Link to="archives">archives</Link></MenuItem></li>
<li><MenuItem><Link to="settings">settings</Link></MenuItem></li>
<li><MenuItem><Link to="featured">featured</Link></MenuItem></li>
)
}
);
You can define any action for your route components.its working fine for me

<Switch> component matching null value in react-router-4

I'm trying to migrate to use React Router 4 and having some trouble understanding the logic of the <Switch> component as it's used in the docs to handle a 404 (or unmatched) route.
For my entry JavaScript file, I have the following routes set up.
index.js
<Switch>
<Route path="/login" component={Login} />
<Route path="/forgot-password" component={ForgotPassword} />
<Route path="/email-verification" component={EmailVerification} />
<Route component={App} />
</Switch>
The Login component will check to see if the user is authenticated, and if so, redirect the user to the /dashboard route (via history.replace).
The App component is only accessible when the user is authenticated and it has a similar check to redirect the user to /login if she is not.
In my App component I have more specified routes that I can be sure are only accessible if the user is logged in.
App.js
<Switch>
<Route path="/dashboard" component={Dashboard} />
<Route path="/accounts" component={Account} />
<Authorize permissions={['view-admin']}>
<Route path="/admin" component={Admin} />
</Authorize>
<Route path="/users" component={Users} />
<Route component={NotFound} />
</Switch>
Herein lies my problem. The Authorize component checks against the permissions passed to see if the user has those permissions, if so, it renders the children directly, if not, it returns null from render().
The expected behavior here is that the <Route path="/admin" /> does not render at all when there are insufficient permissions and the <Route component={NotFound} /> component renders.
According to the docs:
A renders the first child that matches. A
with no path always matches.
However, if I go to any route declared after the <Authorize> component, the router is matching to null. This means that, based on the example above, going to /users returns null. Is the expected behavior of react-router to return the first match in a <Switch/> component, even if it's a null value?
How can I provide a "catch-all" route (404) for such a situation without creating a <PrivateRoute> component for each of the many, authenticated routes in App.js? Should a null value really produce a match?
Unfortunately, react-router's Switch component won't work with routes nested inside other components like in your example. If you check the docs for Switch, it says:
All children of a <Switch> should be <Route> or <Redirect> elements.
... so your Authorize component is not actually legal there as a direct child of Switch.
If you have a read through the source code of the Switch component, you'll see that it rather evilly reads the props of each of its children and manually applies react-router's matchPath method on each child's path (or from) prop to determine which one should be rendered.
So, what's happening in your case is Switch iterates through its children until it gets to your Authorize component. It then looks at that component's props, finding neither a path or from prop, and calls matchPath on an undefined path. As you note yourself, "a <Route> with no path always matches", so matchPath returns true, and Switch renders your Authorize component (ignoring any subsequent Routes or Redirects, since it believes it found a match). The nested '/admin' route inside your Authorize component doesn't match the current path however, so you get a null result back from the render.
I'm facing a similar situation at work. My plan to work around it is to replace react-router's Switch in my routing code with a custom component which iterates through its children, manually rendering each one in turn, and returning the result of the first one that returns something other than null. I'll update this answer when I've given it a shot.
Edit: Well, that didn't work. I couldn't work out a supported way to manually invoke "render" on the children. Sorry I couldn't give you a workaround to Switch's limitations.
In case anyone reads this in >= 2019, one way to deal with this behaviour is to simply wrap the Route-component like so:
import React from 'react'
import { Route } from 'react-router-dom'
type Props = {
permissions: string[]
componentWhenNotAuthorized?: React.ElementType
}
const AuthorizedRoute: React.FunctionComponent<Props> = ({
permissions,
componentWhenNotAuthorized: ComponentWhenNotAuthorized,
...rest
}) => {
const isAuthorized = someFancyAuthorizationLogic(permissions)
return isAuthorized
? <Route {...rest} />
: ComponentWhenNotAuthorized
? <ComponentWhenNotAuthorized {...rest} />
: null
}
export default AuthorizedRoute
Then, simply use it as such:
import React from 'react'
import { Route, Switch } from 'react-router-dom'
import AuthorizedRoute from 'some/path/AuthorizedRoute'
import Account from 'some/path/Account'
import Admin from 'some/path/Admin'
import Dashboard from 'some/path/Dashboard'
import NotFound from 'some/path/NotFound'
import Users from 'some/path/Users'
const AppRouter: React.FunctionComponent = () => (
<Switch>
<Route
component={Account}
path='/accounts'
/>
<AuthorizedRoute
component={Admin}
componentWhenNotAuthorized={NotFound}
path='/admin'
permissions={['view-admin']}
/>
<Route
component={Dashboard}
path='/dashboard'
/>
<Route
component={Users}
path='/users'
/>
<Route
component={NotFound}
/>
</Switch>
)
export default AppRouter
Similar idea to what Robert said, here's how I did it
class NullComponent extends React.Component {
shouldComponentBeRenderedByRoute() {
return false;
}
render() {
return null;
}
}
class CustomSwitch extends React.Component {
render() {
return (
// React.Children.map returns components even for null, which
const children = React.Children.toArray(this.props.children).map(child => {
const { render, shouldComponentBeRenderedByRoute } = child.type.prototype;
if (shouldComponentBeRenderedByRoute && !shouldComponentBeRenderedByRoute.call(child)) {
return null;
}
if (shouldComponentBeRenderedByRoute) {
return render.call(child);
}
return child;
});
return <Switch>{children}</Switch>;
);
}
}
then use it just do
<CustomSwitch>
<Route path... />
<NullComponent />
<Route path... />
</CustomSwitch>
here, a component without shouldComponentBeRenderedByRoute function is assumed to be a valid Route component from react-router, but you can add more condition (maybe use path props) to check if it's a valid Route

Categories