Preface I have no interest in react-router-redux I do not at all want location as part of my state tree.
Original set up was as follows:
let routes = (
<Router history={hashHistory}>
<Route path='/' component={App}>
<IndexRoute component={HomePage} onEnter={_ensureLoggedIn}/>
<Route path="login" component={ SessionForm }/>
</Route>
</Router>
)
let provider = (
<Provider store={store}>
{routes}
</Provider>
);
function _ensureLoggedIn(nextState, replace, callback) {
let hasBeenFetched = store.getState().currentUser.hasBeenFetched;
let loggedIn = !!store.getState().currentUser.id;
if (hasBeenFetched) {
_redirectIfNotLoggedIn();
} else {
store.dispatch(Actions.fetchCurrentUser());
}
function _redirectIfNotLoggedIn() {
if (!loggedIn) {
replace({}, "/login");
}
callback();
}
}
ReactDOM.render(provider, root);
with this attempt, no components have access to the store (the entire point of using provider..?)
Ok cool so some digging reveals that you routes have to be generated inside of provider or else it doesnt work.
I replace {routes} inside Provider with the actual list of routes.
Still doesn't work... the most important aspect here is disabling access to the majority of routes if the user is not logged in. Its very easy to ensure this when all the work is done in the index.js file, but it is going to get messy if I have to start passing the store around (the entire point of using provider........)
Related
I know this issue has been discussed before. But, somehow I cannot get it work in my application.
Normally, the navigation works fine between components. However, history.push only changes the url but does not update the content. For example, in the Login page, I want to navigate user to Home page if already logged in. But, the code only updates the url. Any ideas?
const Login = () => {
useEffect(() => {
if (authenticationService.currentUserValue != null) {
history.push('/home');
}
}, [])
//other code
}
In index.js, I have the following
<BrowserRouter basename={baseUrl} history={history}>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter >,
In app.js, I have:
<Layout>
<Switch>
<PrivateRoute exact path="/home" component={withRouter(Home)} />
<Route exact path='/home' component={withRouter(Home)} />
<Route exact path='/login' component={Login} />
<Route exact path='/register' component={withRouter(Register)} />
</Switch>
</Layout>
The issue in your case is that you are using a custom history with BrowserRouter which isn't correct. BrowserRouter uses its own history and you must use that to change pages
const Login = ({history}) => {
useEffect(() => {
if (authenticationService.currentUserValue != null) {
history.push('/home');
}
}, [])
//other code
}
If you have used custom history for a reason, then you need to use Router with a custom history prop
<Router basename={baseUrl} history={history}>
<Provider store={store}>
<App />
</Provider>
</Router >
everytime before the app loads i check the cookie in order to determine where to navigate.
I check the cookies in a method inside of App.js.
These are the three Routes iam using in the App.js named checkCookies().
import Cookies from "universal-cookie";
import _ from "lodash";
checkCookies();
class App extends Component {
render() {
return (
<Provider store={store}>
<BrowserRouter>
<Switch>
<Route
exact
path="/car"
component={Car}
/>
</Switch>
<Switch>
<Route
exact
path="/car/bmw"
component={BMW}
/>
</Switch>
<Switch>
<Route exact path="Ferrari" component={Ferrari} />
</Switch>
</BrowserRouter>
</Provider>
)
}
}
The code for the checkCookies method is the following:
checkCookies(){
let model = cookies.get("model");
if(_.isEmpty(model) || model==""){
// do this
}
}
when i try to access a cookie in the route "/car" it works but when i try to access the cookie in the route "/car/bmw" or "/car/ferrari" its not working.
But when i rename it from "/car/bmw" just to "/bmw" it works.
But i dont want to change the url name.
The url should stay how it is.
How can i dot it?
I'm trying to redirect logged in user on dashboard page.
Here is my code:
function requireAuth(nextState, replace) {
if(isLoggedIn()) {
console.log("user is logged in");
replace('/dashboard');
} else {
replace('/');
}
}
const Root = () => {
return (
<div className="container">
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route onEnter={requireAuth}>
<Route path="dashboard" component={AllEvents}/>
</Route>
</Route>
</Router>
</div>
)
}
When the user is logged in, my application is running into a loop with a requireAuth method.
Here is the screenshot of the console.
I've already considered two similar questions on StackOverflow which are:
react Maximum call stack size exceeded
React-router, onEnter cause infinite loop with authentication
I've tried both of them, but unfortunately, those examples didn't help me. (I'm also a beginner in React)
Please, tell me what is wrong with my code?
You get an infinite loop because if the user is logged it always redirect him to /dashboard and repeat the redirecting process starting from / and again hitting requireAuth.
Try:
function onlyUnAuthenticated(nextState, replace, callback) {
if(isLoggedIn()) {
replace('/dashboard');
}
callback();
}
function onlyAuthenticated(nextState, replace, callback) {
if(!isLoggedIn()) {
replace('/');
}
callback();
}
const Root = () => {
return (
<div className="container">
<Router history={browserHistory}>
<Route path="/" component={App} onEnter={onlyUnAuthenticated}>
<Route path="dashboard" component={AllEvents} onEnter={onlyAuthenticated}/>
</Route>
</Router>
</div>
)
}
I think that you will have to use callback in the hook.
When setting a react-router app, I often set it on callbacks to represent authorization filters. this leads me to such designs:
# before, in my root component
requireAuth() { ... }
noAuth() { ... }
render() {
return (
<Router history={this.props.history}>
<Route path='/' component={App} onEnter={this.requireAuth}>
<Route path='toys' component={Toys}/>
...
<Route path='/auth' component={Auth} onEnter={this.noAuth}>
...
</Router>
)
}
I am now trying to port this to nodejs and render the page in the server. Suddenly, I'm asked to group all the routes in a const. Second, I lose my root component and those this. callback binds, and am not able to recreate the same architecture on the server.
So my first problem is the route. In react, I can't return groups of components, but a component which encapsulates all the rest. So, this was my attempt:
# root component render
# after import routes from './routes'
render() {
return (
<Router routes={routes} history={this.props.history}/>
);
}
# in routes.js
module.exports = (
<Route> # is this right?
<Route path='/' component={App} onEnter={this.requireAuth}>
<Route path='toys' component={Toys}/>
...
<Route path='/auth' component={Auth} onEnter={this.noAuth}>
...
</Route>
So, this doesn't seem to cut it and I get a few errors. First, I'm encapsulating all routes inside a main Route component. Is this correct, from a react-router perspective? Can I have kind of empty-encapsulating components?
Second would be: How do I bind those this. callbacks on the server side now? Since I'm following the tutorials, My express router looks smth like this:
import routes from './src/components/routes';
...
app.get('*', (req, res) => {
match(
{ routes, location: req.url },
(err, redirectLocation, renderProps) => {
....
But it's breaking with:
onEnter: undefined.requireAuth,
^
TypeError: Cannot read property 'requireAuth' of undefined
Which is right, as routes constant is not bound to any context.
Or is there a more proper way to do this in react-router, and binding callbacks to routes is the wrong approach?
I want to do something like:
<Route>
<MyComponent someCondition={true/false}>
<Route1 />
....
</MyComponent>
</Route
To handle some conditional rendering. However, <MyComponent /> seems not mounted upon rendering.
My question is: can we include normal react component within <Route>? If not, is there a better way to handle conditional routing?
What exactly do you mean by conditional routing? Assuming you mean something like not letting a user hit a route if they aren't authenticated, you can use react-router's onEnter hooks . You can make a parent <Route> that doesn't have a component prop and just handles routing checks. I used some simple onEnter checks in this example.
// onEnter hooks for login and home page to redirect if necessary
const checkAuth = function (nextState, replace) {
const { user } = store.getState()
if (isEmpty(user)) {
replace('/')
}
}
const checkSkipAuth = function (nextState, replace) {
const { user } = store.getState()
if (!isEmpty(user)) {
replace('/home')
}
}
var Index = () => {
return (
<Provider store={store}>
<Router history={history}>
<Route path='/' component={Container}>
<IndexRoute component={Login} onEnter={checkSkipAuth} />
<Route path='home' component={Home} onEnter={checkAuth} />
<Route path='*' component={NoMatch} />
</Route>
</Router>
</Provider>
)
}