React routes gives 404 when trying to hit routes with /:id - reactjs

I inherited a framework for a static website that uses react and react-router-dom for the Routes. We want the website to be able to support dynamic loaded content onto pages via an id.
For example, I have an /articles page that lists all the articles and when you click on one of the listed articles, it opens the url /articles/ABCDEFG and then loads the Article page component and fetches the data for that specific id. This works completely on local dev, however once I deploy to production (hosted as an Azure website application) I get all the pages working except for the ones that have a dynamic URL.
I'm fairly new to react, but from what I've read static react sites can't use the /:id because it requires each page to exist already.
I did a search and tried to remove anything static-related from the server and webconfig.
var express = require('express');
var serveStatic = require('serve-static');
var fallback = require('express-history-api-fallback');
var app = express();
var publicDir = path.resolve(__dirname, '../www');
// app.use('/', serveStatic(publicDir));
//app.use(fallback('index.html', { root: publicDir }));
app.listen(process.env.PORT);
My routes are set up like this
generateRoutes() {
return (
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/about" component={About} />
<Route exact path="/articles" component={Articles} />
<Route path="/articles/:id" component={Article} />
<Route exact path="/careers" component={Careers} />
<Route exact path="/people" component={People} />
<Route path="/profile/:id" component={Profile} />
</Switch>
);
}
What I want is when I put a url into the address bar such as https://example.com/site/profile/246b9e73-5332-4afb-92b8-be5a061bd908 it renders the profile page with that id.
Instead, I get a 404 saying the page can't be found. I'm not sure if I'm chasing a rabbit hole with the static/dynamic idea but I've spent about 2 days trying to figure out why this isn't working so far and I'm willing to try anything.

The deployment section in create-react-app has already addressed this issue, stating
If you use routers that use the HTML5 pushState history API under the hood (for example, React Router with browserHistory), many static file servers will fail. For example, if you used React Router with a route for /todos/42, the development server will respond to localhost:3000/todos/42 properly, but an Express serving a production build as above will not.
This is because when there is a fresh page load for a /todos/42, the
server looks for the file build/todos/42 and does not find it. The
server needs to be configured to respond to a request to /todos/42 by
serving index.html. For example, we can amend our Express example
above to serve index.html for any unknown paths:
So, the solution for you is to change
app.use('/', serveStatic(publicDir));
to
app.use('/*', serveStatic(publicDir));
Hope this helps

Related

React Router match path from query string fallback

I want to be able to serve a React app using React Router both on an Apache and NGINX server without having to change any default Apache/NGINX configs.
The problem is that the application will only be served when you access the root folder, as any sub path leads to a 404 (app.com/ works but not app.com/settings).
For the Apache server, I include a .htaccess file that automatically loads index.html if the requested resource is not found.
For NGINX as far as I understand there is no way to properly load the application in a sub-path unless you change the config file.
My solution for this would be to add the option in the app to store the Router path as a query string, instead of in location.pathname, something like app.com/?page=settings instead of app.com/settings.
Is there any way to add some sort of a middleware in the React router such that when accessing app.com/?page=settings the Router path will be set to /settings. Also, when the user navigates and the router should update the URL in the address bar, it should change it to app.com/?page=user/dashboard instead of app.com/user/dashboard.
I am looking for any solution that would allow the Router to work both with a path and with the path specified as a query string variable.
If needed, here is how my router looks:
<Router history={browserHistory}>
<Route path={BaseRoutes.Home} exact>
<Redirect to={BaseRoutes.Domains} />
</Route>
<Route path={[BaseRoutes.Stats, BaseRoutes.Settings, '/*']} component={SidebarMain} />
<div className={`content-area ${classes.content}`}>
<Switch>
<Route exact path={BaseRoutes.Stats} component={Stats} />
<Route exact path={BaseRoutes.Home} component={Domains} />
<Route exact path={BaseRoutes.Settings} component={Settings} />
</Switch>
</div>
</Router>;
Never mind, I realized I can just use HashRouter instead of Router for NGINX...
So, depending on whether the user's setting of having "pretty" URLs or hash based URLs I can either load the BrowserHistory Router or the HashRouter:
<Router history={browserHistory}> OR <HashRouter>
To switch between them I did:
let SupportedRouter: React.ElementType = HashRouter;
if (MY_CONDITION) {
SupportedRouter = Router;
}
// Render
<SupportedRouter history={browserHistory}>
</SupportedRouter>
I had to cast them to React.ElementType, otherwise optionally passing history lead to TypeScript errors: https://github.com/microsoft/TypeScript/issues/28631#issuecomment-472606019

Using React and Google Hosting, how can I redirect all links that end with /posts/### where ### is any post id

I have a react site that is set up and being used to share deep links into my react native app. That is all working as expected however I'm trying to redirect people who do not have the app installed to the app store / play store.
The url I'm using for deep linking is the following:
https://example.com/posts/###
Is there a way to target all URLs that end with /posts/### and wildcard the id at the end?
Hosted on firebase, built with React.
Thanks!
Using React Router Redirect I was able to solve the issue!
// example
<Router>
<Switch>
<Redirect from="/posts/*" to="/getApp" />
<Route exact path="/getApp" component={GetAppScreen} />
<Route exact path="/contact" component={ContactScreen} />
...
</Switch>
</Router>

Routing for external react component/module

I am building an external React component/module that has many sublevels/pages and routes. I am using React Router V4. This component will be imported into a host application that itself has it's own routing system. The host app is also using React Router V4.
The component's root view is a grid view of cards and when the user click one of the cards it brings them to a detail view of the card. When the user is on a detail view, the url in the browser should change so that a user can bookmark the url of that page and visit that page later.
How should the routing work between the host application and the component? Should the host app pass in the route schema into the component or should the component and host app have it's own separate routing system. Does anyone have any examples of this?
React Router V4 plays very nicely in this situation. Both apps would need their own top level Router component in order to be able to run standalone. But you could organize the code so you can reuse the main switch statement for the SubModule. The urls on the host application would all be prefixed with /subModule/, i.e. /subModule/foo, and they would just be /foo on the subModule standalone application.
HostApp.jsx
<Router>
<Switch>
<Route path="/other" component={Other} />
<Route path="/subModule" component={SubModuleRouter} />
</Switch>
</Router>
SubModule.jsx
<Router>
<Route path="/" component={SubModuleRouter} />
</Router>
SubModuleRouter.jsx
<Switch>
<Route exact path="/foo" component={FooComponent} />
</Switch>

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.

Resources