Manually entering a url refreshes whole react app - reactjs

I have a simple react app I'm working on that utilizes react router. My apps starts out of app.js and has the following router setup in app.js:
<BrowserRouter>
<MuiThemeProvider theme={theme}>
<NavigationBar onLogout={self.userLoggedOut} loggedIn={self.state.loggedIn} />
<div className="content-area container-fluid">
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/login" component={LoginPage} />
<Route exact path="/page1" component={Page1} />
<Route exact path="/page2" component={Page2} />
</Switch>
</div>
</MuiThemeProvider>
</BrowserRouter>
In my app.js constructor, I have console.log to fire off a message to say the constructor was called. Initially I see the message fire off when first coming into the app and then if I use links to navigate between pages I do not see any follow up messages. However, if I go the browser address bar and type in a url manually (http://localhost:3000/login for example), the page loads successfully but have the message from the app.js constructor fire off again. How do you get a react/react-router app to accept manually entered urls the same as links so that the app doesn't reload?

In my app.js constructor, I have console.log to fire off a message to
say the constructor was called. Initially I see the message fire off
when first coming into the app and then if I use links to navigate
between pages I do not see any follow up messages
This is the general behaviour of react app. Very first time you call the link the your app.js enters the tree and it gets rendered. While rendering all lifecycle method including constructor fires in fixed sequence so you are able to see log but when you are changing the link then the component corresponding to route inside is unmounted and component related to new route is rendered and not the complete app.js. This is why you do not see log again.
How do you get a react/react-router app to accept manually entered
urls the same as links so that the app doesn't reload?
You cannot do so because when you are explicitly entering url in address bar then your browser doesnot relate the url with the running web application. It will always render your web application from the beginning. So when it is rendering from beginning you will always get that log.

Related

How to use react-router to make sure it works in Reactjs

Here is my Sandbox code https://codesandbox.io/s/wispy-glitter-spgch?file=/src
what i am trying to do is on click of LOGIN Button trying to rendering the Dashboard page the url does go to the path '/dashboard'but do not render anything
I am not able to find the error
I have done routing in a component and passed that component inside of Context API
On page refresh i.e. in '/dashboard' dashboard section it again goes to login part how to maintain this with state
Use exact parameter in protected route like this:
<ProtectedRoute
path="/dashboard"
exact
auth={authState.isAuthenticated}
component={Dashboard}
/>

Hide React Elements Based on Session Storage or Page

I have React application which has a structure similar to the following.
class App extends Component {
render() {
return (
<div className="App">
<NavBar />
<Router>
<Switch>
<Route path="/login" component={LoginPage} />
<Route path="/" exact component={DashboardPage} />
<Route path="/admin" exact component={AdminPage} />
// many other routes
<Route component={NotFound} />
</Switch>
</Router>
</div>
);
}
}
I do not want the login page to display the <NavBar /> element. I tried using the sessionStorage to get the userId and only display the navigation if the value is set. When I do this and go to the login page and the nav bar is not there. But when I log in, it's still not there. If I refresh however, it will appear.
I know one way to solve this is to make some sort of wrapper around the pages that do want the navigation, but I'd rather not have all of that code duplication, etc.
I feel this must be a common want, and I'm missing something dumb. Any help would be appreciated. I'm new to React so I don't follow everything that's going on here. Thanks.
I think your way of conditionally showing the NavBar is the right way. The question is how to trigger a state change so that the render method takes care of hiding and showing the NavBar, when you log in and out. I suggested maintaining a isLoggedIn state in your App component, and rendering the NavBar based on that, instead of directly accessing the SessionStorage. You could then use a custom event to update the state, when SessionStorage changes.
See this question for updating state based on Storage (in short, you fire and handle a custom event for storage changes): How to listen to localstorage in react.js
This might still be more code that you had hoped for, but it's more aligned with how React works, to derive the view (render) from component state.

React account activation

I'm trying to figure out how to get the account activation link to React.
The Rails API sends an account activation URL as follows:
http://localhost:8080/users/confirm?token=480a476e6be366068dff
I would like to setup a React action that POSTs that token to the API and then a component will render a "account activated" message.
I am currently stuck on 2 issues:
How to directly open the above link in the browser? I'm getting a "Cannot GET /users/confirm" error message. I read that browserHistory is supposed to solve the problem of directly calling React URLs but I'm not sure how to implement it.
How to capture the token from the link? Is "/users/confirm/:token" the correct approach?
routes.jsx:
export default (
<Route history={browserHistory} path="/" component={App}>
<IndexRoute component={HomePage} />
<Route path="/users/login" component={LogInPage} />
<Route path="/users/register" component={RegisterPage} />
<Route path="/users/confirm/:token" component={ConfirmPage} />
</Route>
);
Whatever web server you're using to serve the react code needs to handle that route too. So if you're rendering the html page that bootstraps the react code with rails, add the route to the routes.rb, and have it render the file that loads your bundle.
Now in order to have the token come as a parameter like that:
<Route path="/users/confirm/:token" component={ConfirmPage} />
You'll need to have the rails api direct to it in the same way:
http://localhost:8080/users/confirm/480a476e6be366068dff
If you need to use the query string, update the route in react:
<Route path="/users/confirm" component={ConfirmPage} />
Then in the confirm page, get the token from the query string itself. You can do this in a few ways. I haven't tried it, but I believe react router parses it for you. In the ConfirmPage, access it by:
this.props.location.query.token
Router for Did You have an account? in Material UI ReactJS
handleClickSignIn() {
this.props.history.push("/Register");
}
return(<div><p style={signstyle} > Don't have an account yet?
< a href onClick={this.handleClickSignIn.bind(this)} >Join Register</a>
</p></div>)

React redux routing with hot reloading does not work with refresh

I have the following routes defined
<Route path='/' component={App}>
<IndexRoute component={LoginContainer} />
<Route path='landing' component={LandingComponent} />
<Route path='login' component={LoginContainer} />
</Route>
Now when the user clicks a login button on the loginContainer he is directed to the landing page (from the routes /landing). So now the url changes to
http://server:port/landing
Now if I modify a file and save with hot module reloading (web pack dev server) I get an error saying cannot get http://server:port/landing. This is true because there is no such page, how do I fix this problem.
I am using react-router, react-router-redux, and webpack dev server.
You need to enable historyApiFallback in your webpack dev server config.
This will redirect to the index page on a 404 and consequently allow the router to kick in to find your subroute.
For more info for why this is necessary, or some other, more indepth wy around it, see this answer.

Update property of React component in a separate component

I have a React MaterialUI AppBarcomponent with property title , that I am changing based on the value returned by window.location.pathname. So as the page/url changes, the title will change with it. Looks something like below:
<AppBar
title={this.renderTitle()}
/>
renderTitle() {
if (window.location.pathname === '/home'
return 'home';
} else if (window.location.pathname === '/login'
return 'login';
}
The issue I am running into is that renderTitle() does not get executed if a different component (so not the AppBar) causes the page/url change.
E.g. another separate React component on the page triggers the page to change, which I'd hoped with trigger renderTitle(), but it doesn't... thus the title property never updates. So if I am navigating from /home to /login, the following will happen:
pathname is /home
user presses a button which runs a function, submit(), which is used to change the page w/ react-router
renderTitle() is run at this point, but window.location.pathname is still returning the previous page
submit() changes the page to /login
window.location.pathname is now correctly set to /login, but it is too late as renderTitle() has already been run
any help is appreciated, thanks
The best way is to use react-document-title library.
From documentation:
react-document-title provides a declarative way to specify document.title in a single-page app.
This component can be used on server side as well.
If your component that renders AppBar is an actual route you can just read the pathname from the props that react router injects.
<Router>
<Route path="/" component={App}>
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
<Route path="messages/:id" component={Message} />
</Route>
</Route>
</Router>
For example in App, About, Inbox and Message you have access to the router props. And you can also pass the props to their children.
render() {
return (
<AppBar
title={this.renderTitle(this.props.location.pathname)}
/>
);
}
And in your function just use the parameter to return the correct result. Now because you are using props your component will update automatically when they change.

Resources