Get parent route path from children (react-router-V6) - react-router

V6 related question !
I have nested routing and in child component I have tab component... I need to get path of parent before using navigate(...)
If I click on navigate all works and I`m switching between (/View/test1 and /View/test2 and /View/test3 )
But I need to get parent path before i click on any my tab... Because my tab component use relative path i need to highlight if it is active or not..
Is there a hook to get parent route path? I was searching for something like usegetParrentPath() or some function that can build that route...
function GetTabs() {
return [
{
label: "Test1",
path: `Test1`,
},
{
label: "Test2",
path: `Test2`,
},
{
label: "Test3",
path: `Test3`,
},
] as RouterTabItemType[];
}
function App(){
<Routes>
<Route path "/View/*" element={<View/>
<Routes>
}
function View(){
<Routes>
<Route path "test1" element={<Test1/> // Full path will be /View/test1
<Route path "test2" element={<Test2/> // Full path will be /View/test2
<Route path "test3" element={<Test3/> // Full path will be /View/test3
<Routes>
}
Edited:
I found one solution but reverse way to get fullpath before action...
const resolver = useResolvedPath(to);
//#ts-ignore
const match = useMatch(resolver.pathname);
Using this hook...

Related

Does the routing paths order matter in angular?

I'm trying to make a router for one of my components, but it is not working as expected.
Initially it was working fine, but I had to add another route to decide which mat-tab would be open when redirecting. I added the second route like that, but for some reason the third one stopped working even though the first two were working fine.
import { Routes } from '#angular/router';
import { ActionComponent } from './action.component';
import { ActionResolver } from './action.resolver';
import { ACTION_RESULT_ROUTES } from './result/action-result.routes';
export const ACTION_ROUTES: Routes = [
{ path: ':id', component: ActionComponent, resolve: { action: ActionResolver } },
{ path: ':id/:tab', component: ActionComponent, resolve: { action: ActionResolver } },
{
path: 'action-result',
children: ACTION_RESULT_ROUTES,
},
];
I got a pretty large error when trying the third route, but it starts like this:
ERROR Error: Uncaught (in promise): TypeError: You provided 'null' where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.
Just in case, I tried to reorder it and all three were working fine when I did it like this:
export const ACTION_ROUTES: Routes = [
{ path: ':id', component: ActionComponent, resolve: { action: ActionResolver } },
{
path: 'action-result',
children: ACTION_RESULT_ROUTES,
},
{ path: ':id/:tab', component: ActionComponent, resolve: { action: ActionResolver } },
];
Can anyone tell me why it works like that?
Edit: Added the ACTION_RESULT_ROUTES for clarification
export const ACTION_RESULT_ROUTES: Routes = [
{ path: ':id', component: ActionResultComponent, resolve: { result: ActionResultResolver } },
];
According to Angular:
"The order of routes is important because the Router uses a
first-match wins strategy when matching routes, so more specific
routes should be placed above less specific routes."
It is recommended to have static routes first, therefore your action-result path should go first, followed by the :id/:tab path then :id path last. If you have a wildcard route, it should always be the last route in your array.
The reason behind this logic is that if you had the :id path above the action-result path, angular would use the word 'action-result' as the id in the :id path.
Similarly if you had the :id path above the :id/:tab path, angular would use the words id/tab as the id in the :id path.
So, a rule of thumb is to always put your static routes first, then your dynamic routes from the most specific to the least specific followed by your wildcard route at the end.
E. G.
PATH1
PATH2
PATH3/:USER/:ROLE/:PAGE
PATH4/:SITE/:ID
PATH5:/ID
Wildcard route (*)

How to translate location URL when changing language?

I am currently facing a wall in the localization process of a website. Using i18next, all of our routes are translated and default language has the locale path removed from the URL.
In other words:
/accueil -> /en/home
/produits -> /en/products
and so on...
My issue is when I change the language, the url does not follow (which is to expect, since i18next doesn't talk directly to react-router).
i18next configuration:
i18n
.use(detector)
.use(initReactI18next)
.init({
whitelist: ['fr', 'en'],
fallbackLng: 'fr',
defaultNS: 'common',
detection: {
lookupFromPathIndex: 0,
checkWhitelist: true
},
interpolation: {
escapeValue: false
},
resources: {
en: {
routes: enRoutes,
[...]
},
fr: {
routes: frRoutes,
[...]
}
}
});
fr/routes.json:
{
"home": "/accueil",
"products": "/produits",
[...]
}
en/routes.json:
{
"home": "/en/home",
"products": "en/products",
[...]
}
Router portion in app.jsx:
<Router forceRefresh>
<Switch>
<Route path={`/:locale(en|fr)?${t('routes:home')}`} component={HomeComponent} />
<Route path={`/:locale(en|fr)?${t('routes:products')}`} component={ProductsComponent} />
</Switch>
</Router>
With the following configuration, pages render without issue and easily translate when i18n.changeLanguage is called, but the url doesn't change with it. I've searched everywhere and can't seem to find a go-to approach to translate the url once the language is changed.
I also want to handle a case where the user would change the locale manually directly in the browser url field.
I have tried updating the url on 'languageChanged' event in i18next, but finding the key to the page currently being since adds a lot of complications.
Thx in advance for any help provided.
I finally found an easy and clean method to change the route while also changing the language.
const changeLanguage = (nextLanguage) => {
const routes = i18n.getResourceBundle(i18n.language, 'routes');
const currentPathname = window.location.pathname.replace(/\/+$/, '');
const currentRouteKey = Object.keys(routes).find((key) => routes[key] === currentPathname);
window.location.replace(t(`routes:${currentRouteKey}`, { lng: nextLanguage }));
};
I also needed to change the i18next detection options as follow:
detection: {
order: ['path', ...otherMethods]
lookupFromPathIndex: 0,
checkWhitelist: true
},
I can now safely call this changeLanguage wrapper anywhere and it will handle both the language change (which goes to the default in case it's not part of the url) and the route change.

redux-observable perform transition once the action fires

I am using redux-observable with an epic.
return action$.ofType('APPLY_SHOPPING_LISTS')
.flatMap(() => Observable.concat(Observable.of({ type: 'APPLYING_SHOPPING_LISTS' }), Observable.of({ type: 'APPLIED_SHOPPING_LISTS' }).delay(5000);
Once the epic finishes firing the 'APPLIED_SHOPPING_LISTS' I want to perform a transition, I am using react-router. What is the best place to do this? I saw redux-history-transitions, is this an add-in I should be using?
Further to add to this, I did use redux-history-transitions and change this to the following
return action$.ofType('APPLY_SHOPPING_LISTS')
.flatMap(() => Observable.concat(Observable.of({ type: 'APPLYING_SHOPPING_LISTS' }), Observable.of({ type: 'APPLIED_SHOPPING_LISTS', meta: {
transition: (prevState, nextState, action) => ({
pathname: '/Shopping',
}),
} }).delay(5000);
This does seem to change the url and transition to happen, however the component I have configured under the '/Shopping' path does not render. It just stays on the current page. This is what my route looks like
<Route path='/' component={App}>
<IndexRoute component={LoginContainer} />
<Route path='login' component={LoginContainer} />
<Route path='landing' component={LandingComponent} />
<Route path='Shopping' component={ShoppingPathComponent} />
</Route>
If you're using react-router v3 or before, you can use middleware that will let you push/replace history with redux actions, like react-router-redux. I'm not familiar with redux-history-transitions, but it may (or may not) work similarly.
With react-router-redux, it would just mean emitting the action you want to transition the history, when you want it to happen.
So you'd import the push action creator, and just add it as another action after APPLIED_SHOPPING_LISTS:
Observable.of(
{ type: 'APPLIED_SHOPPING_LISTS' },
push('/Shopping')
)
Altogether, something like this:
import { push } from 'react-router-redux';
const somethingEpic = action$ =>
action$.ofType('APPLY_SHOPPING_LISTS')
.flatMap(() => Observable.concat(
Observable.of({ type: 'APPLYING_SHOPPING_LISTS' }),
Observable.of(
{ type: 'APPLIED_SHOPPING_LISTS' },
push('/Shopping')
)
.delay(5000)
));
If you're using v4, as of this writing react-router-redux is not yet compatible with it, but it's in active development here.

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);
})
},

Nested Routes and browserHistory

I'm having some trouble working with browserHistory and nested routes. The main issue is entering a path directly or refreshing the browser: works perfectly for non-nested routes but not for nested routes.
I'm using webpack-dev-server locally and I am using the history-api-fallback flag:
Without the history-api-fallback flag: any route I type in directly or refresh on gives me "Cannot GET /..." rendered in the browser window. This is expected behavior.
With the history-api-fallback flag: non-nested routes work fine typed in directly or refreshed on but nested routes do not. I don't get the same react-router(?) error rendered in the browser window but I do get 404s in the console, it looks like the browser is trying to load the webpack bundle from one level up in the path (e.g. if I'm on /schools/edit and I hit refresh, the browser tries to load the webpack bundle.js from /schools/ and 404s out).
I have a very simple setup with only a couple of nested routes. All components are simply rendering a div with text for now (with the exception of App and Schools which both also output this.props.children to render their child routes).
My routes:
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="schools" component={Schools}>
<IndexRoute component={SchoolManager} />
<Route path="manage" component={SchoolManager} />
<Route path="edit" component={SchoolEditor} />
</Route>
<Route path="*" component={Home} />
</Route>
</Router>
Webpack config:
entry: [
'webpack/hot/only-dev-server',
'./app/scripts/main.js',
'./app/styles/main.scss'
],
devtool: 'source-map',
output: {
path: './build/',
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.(js|jsx)$/,
loaders: ['react-hot', 'babel-loader?presets[]=es2015,presets[]=stage-0,presets[]=react'],
exclude: /node_modules/
},
{
test: /\.(html|png)$/,
loader: 'file?name=[path][name].[ext]&context=./app'
},
{
test: /\.scss$/,
loaders: ["style", "css", "sass"]
}
]
},
plugins: [
new webpack.NoErrorsPlugin()
],
watchOptions: {
poll: true
}
NPM script to start webpack dev server (run from inside a Vagrant vm):
webpack-dev-server --host 0.0.0.0 --progress --colors --inline --hot --history-api-fallback
To summarize:
<Link>ing to any of the paths above works fine, /schools, /schools/manage, /schools/edit, etc.
Typing in or refreshing on /schools works fine.
Typing in any of the other nested paths (/schools/manage, /schools/edit) or refreshing on them does not work.
I realized as I was typing this out that 'schools' is also technically a nested route but seems to work fine, so it's likely there's something else going on. Thanks in advance for any insight.
After resigning myself to hashHistory for the longest time, finally came across the solution for nested routes. Just had to change the bundle script src from bundle.js to /bundle.js. The leading / ensures that the browser will load bundle.js from the root directory. Credit to Fastas: Unexpected token < error in react router component