react router, match wildcard as parameter - react-router

I have a legacy web app that is being replaced with React and it is called by these various systems by being passed a URL (derived from records in and existing DB system) The app renders folder views, of files in a sandboxed container according to the path passed in.
I need to be able to identify these requests and route them to a File/Folder handing page, treating everything after the '/files/ part of the path down as a parameter to the path of the file or folder.
<Route path="/" handler={Master}>
<Route name="files_link" path="files" handler={Files} />
<Route name="filesWithPath_link" path="files/*:path" handler={Files} />
</Route>
I would like to be able to handle requests passed into
/files
and have it handled in the page (defaulting to top level folder because no path parameter passed.
and have all the following examples of possible URL just passed to the router and extract the path parameter from the bit after /files/.
/files/folder path=folder
/files/folder/filename.ext path=folder/filename.ext
/files/folder/folder1 path=folder/folder1
/files/folder/folder1/filename.ext path=folder/folder1/filename.ext
/files/folder/folder1/folder2/ path=folder/folder1/folder2
/files/folder/folder1/folder2/filename.ext path=folder/folder1/folder2/filename.ext
.... and so on ...
When I try this I get an error
Uncaught Invariant Violation: Missing "splat" parameter for path "/beta/page/files/*:path"
I'm currently using react 0.14.2 and react-router 0.13.4.
How would you go about handling a variable length path in this manner in React-router?

You need to use it like so:
<Route name="filesWithPath_link" path="files/*" handler={Files} />
And then in your React component you can access the value of the splat:
// The '*' in the path match gets assigned to this.props.params.splat
var path = this.props.params.splat;

For completeness, the splat parameter can be accessed from the useParams() hook as property 0:
const splat = useParams()[0];

In React Router v6, the "*" can be accessed from useParams() as a property named *.
const wildcard = useParams()["*"];
And of course you can always get the entire URL (i.e. everything including the bit before the *) using useLocation()

Example in TypeScript with a named parameter:
<Route path="/files/:path">
<Files/>
</Route>
import { useParams } from 'react-router';
export function Files(): React.ReactElement {
const splat = useParams<{ path: string }>().path;
}

Related

displaying mysql data using dynamic url parameters

i have a gallery of images in the main view, and I want to be able to click each image to open a new child view showing details (eg text such as image title). my hardcoded version -- where images and text are stored in frontend -- works. but when I store the data in a MySQL database, i'm running into a problem -- eg unable to drill down into the details when an image is clicked.
the data seems to be fetched properly from backend to frontend because the images of the gallery are showing up. in my mind, the difference (between the hardcoded version and database-connected version) lies largely in react router v4's setup, perhaps a syntax issue that is preventing the paths from matching or preventing data from being passed to the child view:
ReactDOM.render(
..
<Route path="/articles/:PostId" render={(props) => (<Post articles={this.state.blurbs} {...props} />)} />
when it comes to errors, I get different messages depending on the browser:
one says
"TypeError: Unable to get property 'blurbs' of undefined or null reference;"
other says
"TypeError: this.state is undefined"
for brevity, my array state is initialized as such:
class App extends React.Component
..
this.state = {
blurbs: []
}
the value of the array state is updated as such:
componentDidMount()
..
let self = this;
..
return response.json();
...
.then(function(data) {
self.setState({blurbs: data});
})
in child component, data is rendered based on unique id in database as such:
render
..
return
..
{this.props.articles[this.props.match.params.PostId].Title}
so going back, what's wrong with the syntax in react router assuming everything above makes sense? tia
The issue is that the data coming from the API (eg: MySQL) is likely an array. However, you're treating it like a js object.
In your child component's render function, you need to use the findIndex method to find which element of the array that contains the value of your PostId URL parameter (docs). This is assume your table has an id element that is your primary key.
render() {
const idx = this.props.articles.findIndex(x => x.id === this.props.match.params.PostId);
const article = idx > -1 ? this.props.articles[idx] : {};
return (
...
{article.Title}
...
);
to bring "closure" to this thread, i'm sharing what worked for me. instead of passing all props from parent to child via react router, i ended up passing some props from parent to child through the image link.
the issue -- that props was not completely getting from parent to child -- was not immediately clear to me. i narrowed down my issue by tinkering around (eg substituting parts of the project with hardcode to identify the problematic parts of the code).
i found a thread where someone else was experiencing similar blockage in data transfer via react router. after several roadblocks, here are the changes that got me moving forward again.
in parent component, state reference was removed from react router to look as such:
ReactDOM.render(
..
<Route path="/articles/:PostId" render={(props) => (<Post {...props}/>)} />
in parent component, state reference was added to image link to use Link as a data transfer medium as such:
render() {
return (
<div>
{this.state.blurbs.map(image => (
<Link key={image.PostId}
to={{pathname: `/articles/${image.PostId}`,
state: { blurbs: this.state.blurbs }
}}>
<img src={image.src}/>
</Link>
))
}
</div>
in child component, this.props.location.state.blurbs was added as replacement to make the data (originally fetched in parent) accessible to child as such:
render
..
return
..
{this.props.location.state.blurbs[this.props.match.params.PostId].Title}
here's the link to the other thread:
How do i pass state through React_router?

In react-router v4, is it possible to get the deepest route's param out of that Route component

My use case is to have a universal page view statistic function for react-router v4, so it may looks like:
<Provider store={store}>
<Router>
<Tracker>
<App />
</Tracker>
</Router>
</Provider>
My advanced requirement is to get all params from route, so a URL of /users/kitty/books/199231 can be parsed to:
{
path: '/users/:username/books/:isbn',
params: {
username: 'kitty',
isbn: '199231'
}
}
The problem is, my Tracker component can never get access to a nested route's path and match prop, even if I use withRouter with my Tracker component, it gets a path of /
I know in theory my requirement is not correct because we can put two or more <Router> side by side by side, so my real case would be "get the deepest route's params"
Is it possible to archive this? or is there any solution that my page view statistics can parse a URL to it's corresponding route path and params?

React-Router confused about my lifecycle router

After checking out this react-router tutorial, I tried to integrate what I learned here into my project.
My scenario is similar to the number 2 from the tutorial, except that when the user enters /, I want to fetch an api and redirect to the first category comming from the api that looks like this [{'category':'electronics', 'items':[{..}],..},..]
my router looks like this
import RoutaZ from 'Routes.js';
...
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRedirect to={RoutaZ.state.data[0].name} />
<Route path=":category" components={Container, SideNavigation} />
</Route>
my Routes.js looks like this
let Routes = React.createClass({
getInitialState () {
return {
data: null
}
},
componentDidMount() {
var self = this;
fetchData().then(function(results){
self.setState({data: results.data});
})
},
render() {
/* want to return the the first category from api */
return this.state.data[0].name
}
});
In the router, RoutaZ.state.data[0].name returns undefined because the initial state is null. If I set the initial state to [{'category':'hello',...}], it returns hello and redirects to the hello page as expected. What can I do to redirect after the db is fetched?
1) How can I use onEnter from react-router with my current config?
2) How and where can I set a parent component to my router handle all the fetching and pass it to the router as a child?
EDIT: This is just a little part of my application,which is finished but I only have this redirect issue. I know that I can use redux-saga but all my application is already done and would have to redo it completely which I cannot afford.
1- I tried using onEnter but don't know where I should place it.
2-Somewhere in my application is fetched data from the parent component and gave it as props to the child and the child received the data from the api.
Solved it. I had to push the results when the component mounted
componentDidMount() {
var self = this;
fetchData().then(function(results){
router.push(results.data[0].category);
})
},

react-router redirect query string

I am trying to use react-router 2.X to include a redirect logic.
<Route path="albums" component={AlbumList}>
<Redirect from=":albumId" to='/photos/' query= { {'album_id': ':albumId'} } />
</Route>
it seems like :albumId can be extracted from the url in "from" and "to" properties but not in query, how can I use the value of :albumId in query property. Thanks

Colons inside params get querystring parsed on Redirect

"react-router": "^1.0.2"
Here is the Redirect Route:
javascript
<Redirect from="/organize/folder(/:id)" to="/organize/(:id)" />
Where id is of shape someUser:88
So given URL:
http://localhost/organize/folder/someUser:88
We would expect the redirect to take us to:
http://localhost/organize/someUser:88
But instead, it seems that the : is being query string parsed, and instead we end up with:
http://localhost/organize/someUser%3A88
So the parsed string works, but previous versions of react-router would maintain the un-parsed id and pass that to the redirect.
Is there something I may be missing? Perhaps a config option to not auto parse params? I tried with other characters other than :, for example ~, and all the others seem to work.
Perhaps it is somewhere that react-router is parsing for the : for the param and it ends up parsing all of them?
Thanks in advance.
colon is used for parsing variable.
Here is my solution:
function enterIndex(nextState, replace) {
replace('/folder/someUser:88');
}
...
<IndexRoute onEnter={ enterIndex } />
...
or
<Route path="folder/:id" onEnter={ enterIndex } />
Hope it could help you.