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
Related
I have a React App with Routers, I also have 404 component, etc.
But I don't know how to send 404 header to google bot.
I'mm just thinking that it's not possible due React app without SSR.
Am I right?
If you are using Router, you can set your pages like this:
<Router>
<Switch>
<Route exact path = "/home" component={HomePage} />
<Route exact path = "/sign-up" component={SignUpPage} />
<Route exact path = "/login" component={LoginPage} />
<Route exact path="/incorrect-login" component={IncorrectLoginPage} />
<Route exact path = "*" component={PageNotFound} /> //404 Component
</Switch>
</Router>
With this, you set your PageNotFound to the bottom of your router list where if the url of the React App does not correspond to any of your previous urls, then the PageNotFound route is triggered. You can just render your not found component under that page like you would for any other page in your application.
I also used exact so that the url must be exactly the same along with a Switch statement so that only a SINGLE page component can be triggered at once. The * basically just means ALL other urls besides those specified previously.
The PageNotFound is treated as kind of like a default catch statement so it's at the bottom
<BrowserRouter>
<section className='section'>
<Navbar />
<Switch>
<Route exact path='/' component={Articles} />
<Route path= '/football' component={Articles} />
<Route path= '/cooking' component={Articles} />
<Route path= '/coding' component={Articles} />
<Route path= '/article/:id' component={ArticleById} />
<Route path= '/articles/comments' component={ArticleComments}/>
<Route component={NoMatch} />
</Switch>
</section>
</BrowserRouter>
so the route '/article/:id' and '/articles/comments' fails to load anything when i take thhe '/:id' or the '/comments' off the path the components load.
The problem is that your project is NOT using server side routing. I placed a component in your parent Component (Articles) and clicked that link (which was set to go to article/1
e.g.
<Link to="/article/1">CLICK HERE</Link>
and it worked just fine. However, type in the URL bar
http://localhost:9000/article/1
does not work...here's why...
If you don't have server side routing setup, then when you make a BROWSER based request (which is what you're doing via the URL bar) it will send off a request for the content at the location in the URL. The reason you're getting 404 NOT FOUND errors is because you're expecting webpack-dev-server to know that when you type /articles/1 it should serve a component, but as far as it can tell you've actually requested a folder called articles with a file called 1.
Because of this, you need a server to handle browser requests for locations.
You need a static router to match the request url to a path on your routes file which will in turn "return" that component to the browser.
When you use a
<Switch> in React Router it will render the FIRST component that matches that path.
Is it rendering your NoMatch component when you have :id?
For /comments you should use a subroute e.g.
<Route path='/articles'>
<Route path='/comments' component={ArticleComments} />
</Route>
As #Joshua Underwood says, the fact is webpack-dev-server try to fetch your script in nowhere. But I don't think it is the correct answer based on the fact that other route is work perfectly. So, that is to say the problem is neither due to webpack-dev-server or react but your index.html itself.
I bet you have set historyApiFallback: true in webpack.config.js, and also you point your bundle.js something like bundle.js or .\bundle.js, which you should set it as \bundle.js. Otherwise, although the server handle the fallback correctly, but your index.html file have no idea which path it was found, and try to anchor your script based on the wrong path level.
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>)
I'm using React/Redux/Router but this is really just a router question and one I can't seem to find the answer for. Essentially, I want my app to only kick in if the '#search' hash is present in the URL, otherwise don't do anything.
If I use something like the following I get an error (did not match any routes) when I go to the root URL '/'.
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="#search" component={HelloWorldContainer} />
</Router>
</Provider>,
document.querySelector('.body-wrap'),
);
Any help appreciated...
That's because you haven't provided a route for the root '/', which is the default entry point to a web application. I'm not sure why you wouldn't provide a route for the root '/' but React Router will continue to throw errors for routes it can't match. You can provide an entire component as a catch-all for routes that don't match what you've specified; like so:
<Route path='*' component={ErrorPage}/>
Also, a request to the route path='#search' must be exact to match. So a request to /#search123 will throw an error, unless you have the catch-all route above.
I have a particular situation where I would like to load a different route rather than the initial request route while keeping the app universal/isomorphic.
So I have some routes like so
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="potato" component={Potato}/>
<Route path="apple" component={Apple}/>
</Route>
</Router>
On the initial request of / the server decides based on some other information elsewhere what other route should be loaded instead, ie the apple route should be rendered instead but the url on the front end should remain /
At first this is quite trivial as you can just change the url provided to match
match({ routes, location: url }, (error, redirectLocation, renderProps) => {
However, the client first displays correctly then a few seconds after it loads the original request route rather than what the server decided.
It would display an error like this
Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
This makes sense as I told the server to render the /apple when the / route was hit. But when it gets to the client side it goes 'Um the route is / but the markup is showing something different to what should be there.
So is there anyway I can fool the client that everything is well and correct?
One solution is just do a 301 redirect but this means the client side
will end up showing /apple rather than just / in the url
I'd like to avoid a 301 for SEO and to keep the initial url route that the user typed
Any solution for this bonkers request?
I'm using
react:15.0.2
react-router: 2.4.0
react-redux:4.1.2
redux: 3.1.2
You can define the routes on the server before calling match and let the client pick them up on the client.
Something like this on the server:
function getRoutes(something) {
if (something === 'apple') {
return (
<Route path="/" component={Apple}>
<Route path="potato" component={Potato}/>
</Route>
);
}
return (
<Route path="/" component={App}>
<Route path="potato" component={Potato}/>
<Route path="apple" component={Apple}/>
</Route>
);
}
const routes = getRoutes('apple');
<script dangerouslySetInnerHTML={{__html: `window.__INITIAL_ROUTES__ = ${routes}`}}></script>
<Router history={createMemoryHistory()}>
{routes}
</Router>
...and on the client:
const routes = window.__INITIAL_ROUTES__;
<Router history={browserHistory}>
{routes}
</Router>