redux-observable perform transition once the action fires - react-router

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.

Related

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

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...

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.

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

Can I set a base route in react-router

Assuming my app's base url is example.com/app
Is it possible to set a base route in react-router so instead of writing all routes as
/app/a
/app/b
/app/c
I can just specify them as
a
b
c
I tried the below example I found in the docs but it wouldn't work (page would display nothing). Maybe it's because I'm using react-router#3.0.0-alpha.1, or I'm doing something wrong.
import { useRouterHistory } from 'react-router'
import { createHistory } from 'history'
const history = useRouterHistory(createHistory)({
basename: '/app'
})
const Root = ({store}) => (
<Provider store={store}>
<Router history={history}>
<Route path='/' component={App}>
...
</Route>
</Router>
</Provider>
)
With the newest react router (v4) you can easily do this
<BrowserRouter basename="/calendar">
<Link to="/today"/> // renders <a href="/calendar/today">
</BrowserRouter>
If you want to use <Router />, that give you access to history object, allowing you to change page through history.push('/my-path') method directly from js. You will face the issue that BrowserRouter does not have history prop available, and Router does not have basename available.
The solution is the following:
const App: React.FC = () => {
// do not put a slash at the end of the basename value.
const history = createBrowserHistory({ basename: '/your-base-name' });
return <Router history={history}>
...
</Router>;
}
The base URL for all locations. If your app is served from a sub-directory on your server, you’ll want to set this to the sub-directory. A properly formatted basename should have a leading slash, but no trailing slash
https://reacttraining.com/react-router/web/api/BrowserRouter/basename-string