Why can't I use querystrings in my router? - react-router

I want to use the url path with the query string in the router as shown below.
//App.js
<Route
path={"/my_account"}
component={Pages.account}
/>
<Route
exact
path={"/support_main/notice?page=1"}👈 Lik this!!!!!!!!!
component={Pages.support_notice_list}
/>
// link.js
<Link to={"/support_main/notice?page=1"} className="menu-list-link">
<ListItemText
primary={item.text}
className="munu-list-text"
/>
In this case, the page cannot be found.....:(
//App.js
<Route
exact
path={"/support_main/notice"}👈 OK
component={Pages.support_notice_list}
/>
// link.js
<Link to={"/support_main/notice?page=1"} className="menu-list-link">
<ListItemText
primary={item.text}
className="munu-list-text"
/>
In the above case, it works normally.
Is there any way to use query string in router ?

Simply put, it's because RRDv5 deals only in matching the path, not any part of the query search string.
For route path matching, only the "/support_main/notice" is considered. To make this a bit more clear, here is a more conventional way to link to "/support_main/notice" and pass a search string. Here the path and search string are split up and explicitly passed.
<Link
to={{
pathname: "/support_main/notice",
search: "?page=1"
}}
>
To notice
</Link>
When you link to "/support_main/notice?page=1" the search isn't part of the path matching.
Link to-object
to: object
An object that can have any of the following properties:
pathname: A string representing the path to link to.
search: A string representation of query parameters.
hash: A hash to put in the URL, e.g. #a-hash.
state: State to persist to the location.
<Link
to={{
pathname: "/courses",
search: "?sort=name",
hash: "#the-hash",
state: { fromDashboard: true }
}}
/>
Consider the following code:
const Page1MatchPath = () => <h1>Page 1 matched by path</h1>;
const Page1MatchPathAndQuery = () => <h1>Page 1 matched by path and query</h1>;
export default function App() {
return (
<Router>
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<ul>
<li>
<Link to="/support_main/notice?page=1">To notice (string)</Link>
</li>
<li>
<Link
to={{
pathname: "/support_main/notice",
search: "?page=1"
}}
>
To notice (object)
</Link>
</li>
</ul>
<Switch>
<Route
path={"/support_main/notice?page=1"}
component={Page1MatchPathAndQuery}
/>
<Route path={"/support_main/notice"} component={Page1MatchPath} />
</Switch>
</div>
</Router>
);
}
Note that even though the full path and query string is listed first in the Switch that is doesn't match, and is skipped over and matched by path alone. In other words, it's too specific to match "/support_main/notice", and yet doesn't take the query string into consideration for matching.
But why don't query strings work in path prop?
This is very likely due to the way path matching is handled. Under-the-hood RRDv5 path props are strings that path-to-regex#^1.7.0 understands. In this specific case the "?" character is interpreted as an optional parameter.
Optional
Parameters can be suffixed with a question mark (?) to make the
parameter optional. This will also make the prefix optional.
var re = pathToRegexp('/:foo/:bar?', keys)
// keys = [{ name: 'foo', ... }, { name: 'bar', delimiter: '/', optional: true, repeat: false }]
re.exec('/test')
//=> ['/test', 'test', undefined]
re.exec('/test/route')
//=> ['/test', 'test', 'route']
To also more firmly put it, the Usage outright also states in a note:
Please note: The RegExp returned by path-to-regexp is intended for use with pathnames or hostnames. It can not handle the query
strings or fragments of a URL.

Related

Property 'from' does not exist on type 'IntrinsicAttributes & NavigateProps' - react-router-dom v6

I migrated from react-router-dom 5 to version 6 and the Navigate you see below used to be Redirect. The problem is that from from from={from} (the first one) errors saying
Type '{ key: string; from: string; to: any; }' is not assignable to
type 'IntrinsicAttributes & NavigateProps'. Property 'from' does not
exist on type 'IntrinsicAttributes & NavigateProps'.
I don't know if it has to be replaced with something else from v6 of react-router-dom but any help will be greatly appreciated.
const redirects = routeAliases(routes).map(({ from, to }) => {
return <Navigate key={`${from}-${to}`} from={from} to={{ ...location, pathname: to }} />;
});
In react-router-dom v6 to fully replicate the v5's Redirect component with from prop you must render the Navigate component in a Route component.
<Route path={fromPath} element={<Navigate to={toPath} replace />} />
Applied to your code snippet:
const redirects = routeAliases(routes).map(({ from, to }) => (
<Route
key={`${from}-${to}`}
path={from}
element={<Navigate to={{ ...location, pathname: to }} replace />}
/>
));

React use declared const in other file

I've tried to search around for a solution, but I can't find anything that helps.
I want to be able to take the details from the api and be able to pass it's information to another page. The name is currently defined like "drink.strDrink" and so is the image and so on.
But I want to get the date in another document after fetching it, here's what I've got.
// Here I get the data from the API
const getDrink = () => {
Axios.get(`https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${id}`).then((res)=> {
console.log(res)
setDrinkList(res.data.drinks)
})
}
// Here I use the data from the API
<Fragment key={index} >
<Card.Img variant="top" src={drink.strDrinkThumb} />
<Card.Body>
<Card.Title>{drink.strDrink}</Card.Title>
<Card.Text style={{overflow: 'hidden', whiteSpace: 'pre'}}>
<p>{drink.strCategory}</p>
</Card.Text>
<Router>
<Button variant="primary"><Link to={"/drink#"+drink.strDrink.replace(/\s+/g, '-')}>More info</Link></Button>
</Router>
</Card.Body>
</Fragment>
You can either use a state management tool like redux or the context api:
https://react-redux.js.org/
https://reactjs.org/docs/context.html
Or you can lift state up and pass the state as props:
https://reactjs.org/docs/lifting-state-up.html

react-router show different when component is wraped

I'm new react user, when i see react-router docs, I confused.
let me show,
first, the docs url: https://reacttraining.com/react-router/web/example/route-config
I simplify like this
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
const routes = [
{
path: "/",
component: Sandwiches
},
{
path: "/tacos",
component: Tacos
}
];
export default function RouteConfigExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">/</Link>
</li>
<li>
<Link to="/tacos">tacos</Link>
</li>
</ul>
<Switch>
{routes.map((route, i) => (
<RouteWithSubRoutes key={i} {...route} />
))}
</Switch>
</div>
</Router>
);
}
function RouteWithSubRoutes(route) {
return (
<Route
exact
path={route.path}
component={route.component}
/>
);
}
function Sandwiches() {
return <h2>/</h2>;
}
function Tacos() {
return (
<div>
<h2>Tacos</h2>
</div>
);
}
now, when i click / show /, but click Tacos show nothing.
what expect, click / show /, and click Tacos show Tacos.
I resolve by these
// first, do not use component wrap
export default function RouteConfigExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">/</Link>
</li>
<li>
<Link to="/tacos">tacos</Link>
</li>
</ul>
<Switch>
{routes.map((route, i) => (
<Route
key={i}
exact
path={route.path}
component={route.component}/>
))}
</Switch>
</div>
</Router>
);
}
// second, do not use Switch
export default function RouteConfigExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">/</Link>
</li>
<li>
<Link to="/tacos">tacos</Link>
</li>
</ul>
{routes.map((route, i) => (
<RouteWithSubRoutes key={i} {...route} />
))}
</div>
</Router>
);
}
It bothered me for a long time, now I'm dying to know why, Please tell me in detail, and thanks so much...
In addition, My English is not pretty, understanding...
Switch components only valid children are Route and Redirect, so even though another react component renders a Route it won't work quite in the same way.
Switch children
All children of a <Switch> should be <Route> or <Redirect> elements.
Only the first child to match the current location will be rendered.
Edit
While wrapped Route components can still render, as "grand-children" descendants, it appears that route props are only applied at the root Switch level.
In the following Switch, even though RouteWithSubRoutes specifies the exact prop, RouteWithSubRoutes in the react DOM does not, so only the first element is returned (coincidentally the home "/" route).
<Switch>
{routes.map(route => (
<RouteWithSubRoutes key={route.path} {...route} />
))}
</Switch>
This following Switch is identical to the above except for specifying exact prop, and this works as expected.
<Switch>
{routes.map(route => (
<RouteWithSubRoutes key={route.path} exact {...route} />
))}
</Switch>
/Edit
It sounds like you have a two-part question, why both resolutions work.
The first attempt at fixing by rendering a Route directly succeeds by specifying the exact prop on all routes within the Switch, which only matches and renders the first matching Route or Redirect. When the path is exactly "/" then that component renders, and when it is exactly "/tacos" then that component renders.
The second attempt you render all the routes just right in the Router, which matches and renders all matching routes, but since you specify, again, the exact prop, it matches a single route and works.
Demo

How to have react route path one/two/three?

I would like to have something like this
<Route
path="/one/two/three"
render={() => (
<Component/>
)}
exact
/>
But it does not seem to work locally when I wrap it all into a Router. I only see the contents of the Component when it is just /one, but I would like to have /one/two/three as the path (being my root - user lands on that path).
In react-router-v4 you can have below routes only,
<Route path="/one" render={() => (<Component/>)} exact /> //Direct route without any params
or
<Route path="/one/:two" render={() => (<Component/>)} exact /> //Route with params, here `:two` is the parameter to route
If you want to use routes like in your example, then this can be achieve using BrowseRouter basename attribute,
<BrowserRouter basename="/one/two">
<Route path="/three" render={() => (<Component/>)} exact />
</BrowserRouter>
And you Link should be like,
<Link to="/three"/> // renders <a href="/one/two/three">
Few reasons why this did not work for me.
I was missing output: { publicPath: '/' }, in my webpack config.
And weirder thing was that I was using PureComponent instead of Component which totally broke the rending when redirecting to the next step with similar path(x/y/z) or using Link.

React-router and jsx-no-bind

I'm using react-router to do navigation. I have some components that are requiring a login before they can be seen.
Therefore I have this: (from following documentation in react-router)
<Route
{...rest}
render={props =>
isLoggedIn()
? <Component {...props} />
: <Redirect
to={{
pathname: '/login',
state: {from: props.location},
}}
/>}
/>
However, there is a problem with this, since jsx-no-bind disallows arrow functions. What is the correct way around this? Or should this simply be ignored because it for some reason does not suffer the same performance hits?
Official React documentation on render props:
https://reactjs.org/docs/render-props.html
This documentation uses examples like:
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>
Be aware of caveats as mentioned here:
https://reactjs.org/docs/render-props.html#caveats
This includes the use of PureComponents