React-router messes up request urls - javascript

I am trying to implement the following nested urls with react-router. My problem is that the Feed component sends both GET and POST requests to the wrong url when nested within the Router component, like this:
ReactDOM.render((
<Router history = { browserHistory }>
<Route component={ NavBar }>
<Route path='/' component={ Feed } url='/api/threads' pollInterval={ 2000 } />
<Route path='signup' component={ Signup } />
</Route>
</Router>
), document.getElementById('root'));
sends requests to http://localhost:3000/?_=1463499798727 which returns the content of index.html, which causes an error since the ajax request is expecting json data, not html, and is wrong anyway.
while
ReactDOM.render((
<Router history = { browserHistory }>
<Route component={ NavBar }>
<Route path='signup' component={ Signup } />
</Route>
</Router>
), document.getElementById('root'));
ReactDOM.render(<Feed url='/api/threads' pollInterval={ 2000 }/>, document.getElementById('feed'))
sends requests to the expected url http:localhost:3001/api/threads and returns data, and everything works normally.
As a side note, I have port 3000 set for the webpack-hot-load frontend and port 3001 set for the Express backend.
On the Express side, I have the following route set:
router.get('/api/threads', function (req, res, next) {
Thread.find(function (err, threads) {
if (err) { return next(err); }
res.json(threads)
})
})
Visiting localhost:3001/api/threads returns the expected data.
localhost:3001 returns Cannot GET '/' which is expected.

First, if a URL is intended to be used as an API endpoint and not directly in the browser then it probably does not belong in your react-router at all. Only put paths in the router that you expect to render a view in the browser. So if you want localhost:3001/api/threads to return JSON via an API call, take it out of your router.
Also, you use should organize your routes using IndexRoute. Try this:
<Router history={browserHistory}>
<Route path="/" component={CoreLayout}>
<IndexRoute component={Feed} />
<Route path="signup" component={Signup} />
<Route path="*" component={NotFoundView} />
</Route>
</Router>
Where CoreLayout simple renders it's children. I'm not sure exactly what you are trying to display for the root URL (localhost:3001) but you would use a component like above.
To use your API endpoint you can just call it in the component directly via it's full path (localhost:3001/api/threads).

Related

REGEXP help need to render 404 path

I'm trying to use REGEXP to help render a 404 page in a react app using the path. I cannot figure it out.
This is the REGEXP I want; I want to match IF:
The entire path is !== '/' && does not contain the word 'article'.
EDIT: Including code for clarification:
<Switch location={location} key={location.pathname}>
<Route path={["/commentisfree/article/:id", "/commentisfree"]}>
<Opinion />
</Route>
<Route path={["/sport/article/:id", "/sport"]}>
<Sport />
</Route>
<Route path={["/culture/article/:id", "/culture"]}>
<Culture />
</Route>
<Route path={["/lifestyle/article/:id", "/lifestyle"]}>
<Lifestyle />
</Route>
<Route path={["/signup", "/login"]}>
<Account />
</Route>
<Route path={["search/:search", "/search"]}>
<Search />
</Route>
<Route path={/REGEXP[if path !== "/" && path does NOT include the word "article"]/} component={ErrorDefault} />
<Route path={["/article/:id", "/"]}>
<Home />
</Route>
</Switch>
At the bottom you'll see I've had to put the 404 component just above my index route because the 404 is never hit if I use a catch all route, it simply navigates to the index route but maintains the incorrect URL.
For example, entering http://localhost:3000/mistake takes me to the index route. I don't want this as I rely on the urls to render modular components and it doesn't work if the path is incorrect.
So i need to force any non declared paths to a 404 page where i can display a message and a link back to the home page.
Are you using React router?
If so, you could use a catch-all route at the end of the route list, which will render a 404 page, like here: https://reactrouter.com/web/example/no-match
Edited: added link to React router documentation
The following function uses Regular Expression
const checkRegex = (urlString) => {
return (
new RegExp("/").test(urlString) && new RegExp("article").test(urlString)
);
};
You can also use the includes function for this
const checkRegex = (urlString) => {
return (
urlString.includes('article') && urlString.includes('/')
);
};

React nested routing doesnt work only when deploy on GCP nginx server

I am new to React. I created a nested route which works perfectly on local across different devices. However, when I deploy it on GCP nginx server, the nested routing seems to have been ignored if I directly visit the path. Contrastly, it seems to work well when the path was pushed by a button, which I do not understand.
I have a routes.js
<Router history={history}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/admin" component={Admin} />
<ProtectedRoute path="/message" component={Message} />
<Route path="/profile" component={Profile} />
<Route path="*" component={() => <h1 style={{ textAlign: "center" }}>404 NOT FOUND</h1>} />
</Switch>
</Router>
Taking in /profile, I have nested routes
const { path, url } = this.props.match;
...codes between...
<Switch>
<Route path={`${url}/seller`} component={SellerProfile} />
<ProtectedRoute path={`${url}/user`} component={UserProfile} />
<Route path={`${url}/motto`} component={mottoProfile} />
<Route path={`${url}*`} component={() => <h1 style={{ textAlign: "center" }}>404 NOT FOUND</h1>} />
</Switch>
Again, locally, all the components were rendered correctly. However, when deployed on server, visting the nested routes (ie /profile/motto) directly would return a blank page.
Despite that, when I use history.push, I am able to visit those nested routes. For instance, I have a function in another component that can successfully visit the nested routes.
const onProfile = (e) => {
const cookies = new Cookies();
props.history.push({
pathname: "/profile/user",
state: { userid: this.state.userid },
});
}
Note that profile/motto does not require a state and its not the only nested routes that behave the same.
During my search I was taken here - it's not a direct answer about nginx, but for GCP load balancer in front of a bucket. Posting since others might be looking for the same.
Here is good background information: react router doesn't work in aws s3 bucket
What worked for me is to rewrite url with /index.html similar to forwarding the 404 errors solution above. This works clean and returns 200 code, but only works for 1 level, so paths "/", "/users" work, but path = "/user/*" and trying "/user/someuser" does not work.
Here is the url map:
yourmapname.yaml:
defaultService: https://www.googleapis.com/compute/v1/projects/yourprojectname/global/backendBuckets/yourbucketname
hostRules:
- hosts:
- yourhostname
pathMatcher: path-matcher-2
kind: compute#urlMap
name: yourmapname
pathMatchers:
- defaultService: https://www.googleapis.com/compute/v1/projects/yourprojectname/global/backendBuckets/yourbucketname
name: path-matcher-2
pathRules:
- paths:
- /
- /user
- /users
service: https://www.googleapis.com/compute/v1/projects/yourprojectname/global/backendBuckets/yourbucketname
routeAction:
urlRewrite:
pathPrefixRewrite: /index.html
cloud compute url-maps import yourmapname \
--source ./gcp/yourmapname.yaml \
--global
Same principle of rewriting to index.html works in nginx.

React Router: <Redirect push> doesn't update browser url

I'm learning React making a small single page app. Just added react-router-dom today and building it out to do routes and private routes. All is well except for one thing: When the user enters a malformed url in the browser bar, the user should be rerouted to the index (WORKS!), but the browser url bar is not updated on this redirect. Oddly enough, when I hit a private route while not authorized, the redirect DOES update the url bar. What am I missing?
router.js:
const PrivateRoute = ({auth: authenticated, component: Component, ...rest}) => (
<Route {...rest} render={(props) => (
authenticated === true
? <Component {...props} />
: <Redirect to='/login/'/>
)}/>
);
export default function Router() {
const auth = useSelector(isAuthenticated);
return (
<Switch>
<PrivateRoute auth={"auth"} path={"/dashboard/"} component={DashboardContainer}/>
<Route path={"/about/"} component={AboutContainer}/>
<Route path={"/login/"} component={LoginContainer}/>
<Route path={"/terms/"} component={TermsContainer}/>
<Route path={"/"} component={IndexContainer}/>
<Redirect push to={"/"}/>
</Switch>
);
}
I believe your issue is a result of not specifying that the paths should be exact matches, therefore any route will match with your route that is specified as:
<Route path={"/"} component={IndexContainer}/>
Try adding the exact prop to all of your routes (except for your redirect), and you should properly get redirected to the home page with the correct URL.
More details on the exact prop here: React : difference between <Route exact path="/" /> and <Route path="/" />

Multiple path names for a same component in Reach Router

I am using the same component for three different routes:
<Router>
<Home path="/" />
<Home path="/home" />
</Router>
Is there anyway to combine it, to be like:
<Router>
<Home path=["/home", "/"] />
</Router>
For Reach Router: (https://reach.tech/router/example/)
With the exact sample shown, the only way I can see how to do this(on a single line) is with a wildcard.
To find a way to reproduce this without side effects, we would need to see the entire nav menu.
<Router>
<Home path="/*" />
<Chicken path="chicken">
</Router>
...
const Home = props => {
let urlPath = props["*"]
// URL: "/home"
// urlPath === "home"
// URL/: "/"
// urlPath ===""
}
You could continue with other paths below Home and the router would allow them to process.
Check out the the example using a wildcard and reach router on codesandbox, I wrote!
Note: This is a catch-all, but without parsing a parameter is the only single line solution I saw.
Some DrawBacks include Home rendering instead of '404', etc.
//This could be resolved with an if statement in your render
//It will not produce the intended URL either for /home, and I have not looked into that since it is not part of the question.. but if it matched props[*] I'm sure you could redirect or something.
You can read more about the Route Component for Reach Router.
https://reach.tech/router/api/RouteComponent
I wasn't happy with the wildcard solution from the documentation and #cullen-bond because I had to map many other paths and came up with this solution:
<Router>
{["/home", "/", "/other", "/a-lot-more"].map(page => <Home path={page} />)}
</Router>
Example: https://codesandbox.io/s/reach-router-starter-v1-forked-6f44c?file=/src/index.js
Depending on the situation you're dealing with, <Redirect /> could also make the work.
<Router>
<Redirect from="/" path="/home" noThrow />
<Home path="/home" />
</Router>
You can use a single component for mutiple paths, by using a array of routes.
code example :
import sampleComponent from './sampleComponent'; // single component for mutiple routes
<Router>
<Switch>
{["/pathname_1", "/pathname_2", "/pathname_3", "/pathname_4", "/pathname_5", "/pathname_6"].map(pathname => (<Route exact path={pathname} component={sampleComponent} />) )}
<Switch>
<Router>

Why can I enter URL directly in adress's bar with hashHistory but not with browserHistory?

I'm currenly discover React-Router and I have a question, why can I enter URL directly in adress's bar with hashHistory but not with browserHistory ?
here the url I try to reach :
localhost:3000/element/createform
here my code :
// import {...}
// then the code content
<ApolloProvider client={client} >
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={ElementList}/>
<Route path="element/createform" component={CreateForm} exact/>
</Route>
</Router>
</ApolloProvider>
With browserHistory it returns me "cannot get path" but with hashHistory it works. So why ?
You need your web server to always return the main HTML file.
If you call example.com/index.html/test your browser will request example.com/index.html and then let index.html deal eith the hash (at least here).
If you call example.com/index.html/test your browser will request another file called test, which will not be found on the server.

Categories