What is the meteor-reaktor equivalent for react-router? - javascript

In MeteorJS one can choose to use Flowrouter, react-router, or many other options for routing. My application is using react-router. In the meteor-reaktor package there is an option for <Route> called triggersEnter() that runs a specified function when the user loads the route.
Example
Reaktor.init(
<Router>
<Route
path="/"
name="homePage"
layout={Layout}
content={BlogList}
triggersEnter={homePageLogger}/>
</Router>
);
function homePageLogger(context, redirect) {
if(context.queryParams.forwardToBlog) {
redirect("/blog");
}
}
How can I do this in react-router? I did not see anything related to this in the documentation and I can not seem to find a package that does this. Can someone point me in the right direction?

You can use onEnter:
<Route path="/" component={MyComponent} onEnter={componentToRedirectTo}>

Related

What exactly is <switch> used for in React Router?

I am new to React learning , and was trying to build an app using react-router-dom. I was able to implement basic routing when I came across the term 'switch'. Can anyone please explain me with a use-case example where we use switch and what is its use?
Since you are new am going to take a bit more time to explain with examples and also add some few things about it you may want to have handy.
So like Iddler said, Switch is more or less like the "Switch case" condition in any other languages but usually ends with the first match it finds.
<Switch>
<Route path="/home" component={Home} />
<Route path="/about" component="{About} />
</Switch>
That is an example of its most basic use. Switch determines the start and end of the block or condition. Each Route checks for the current path. supposing we were working on "www.test.com". All of "www.test.com" is the root "/". So the Route checks for the path after the root. so if you had "www.test.com/home", "/home" comes after the root so the "Home" component will be loaded in our example above and if you had "www.test.com/about" the "About" component is loaded.
Be mindful that you can use any names. the components and paths do not need to be the same.
There are also cases when you might want to use exact to match an exact path. this is useful when you have similar paths. eg "/shop" and "/shop/shoes". using exact ensures Switch matches exact paths and not just the first.
Eg:
<Switch>
<Route exact path="/shop" component={Shop} />
<Route exact path="shop/shoes" component="{Shoes} />
</Switch>
You can also use <Route... /> without the <Switch>.
Eg:
<Route path="/home" component={Home} />
so unlike direct component loads where you just load a component like <Home /> Routers work with the URLs.
Lastly, the <Route... /> path can take arrays of url to load same component.
Eg:
<Switch>
<Route path={[ "/home", "/dashboard", "/house", /start" ]} component={Home} />
<Route exact path={[ "/about", "/about/management", "/about/branches" ]} component="{About} />
</Switch>
I hope this helps. Let me know if you need clarifications of any sort. :)
UPDATE:
You are not required to write Routers in this same format always. below is another format you could use;
<Router>
<Switch>
<Route path="/home">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
</Switch>
</Router>
There are instances like am in now where you want to be able to handle what shows when a wrong URL is entered. like a 404page. you could use Router without a path. so like a regular switch statement, that becomes your default.
<Switch>
<Route path="/home" component={Home} />
<Route path="/about" component="{About} />
<Route component="{PageNotFound} />
</Switch>
Switch looks through Route's children and renders the first one that matches the current path, once it does it will not look for any other matches.
The Switch component will work much in the same way as the Router component, meaning we will still have nested Route components that need exact paths, etc.
The added functionality of Switch is that it will only render the first matched child. This is really handy when we have nested routes such as the below:
<Switch>
<Route path="/accounts/new" component={AddForm} />
<Route path={`/accounts/:accountId`} component={Profile} />
</Switch>
Say we put the above code in a component — we would see that both {AddForm} and {Profile} would render, since “/accounts/new” could look like either Route to a Router component. Router components render inclusively of all route matches. The Switch component will render exact matches, and only the exact match. This makes it ideal for these nested scenarios.

React query strings take more than 1 parameter?

I'm working with React.js Router and trying to achieve this:
Users go to a module then a level and the url will look like this:
myapp.com/game/module/some-module/level/level-1.
I need to handle all different module and level like this:
/module/:name
/level/:name
so I don't need to specify each url several times.
Here's the code in App.js:
const App = () =>
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/game" component={Game} />
<Route exact path="/module/:name" component={Module} />
<Route exact path="/level/:name" component={Level} />
<Route component={NoMatch} />
</Switch>
</Router>
export default App
I know I can "grab" the value of module name in the child like this: match.params.name.
How do I do it in React?
Do you have a better approach than this?
e.g. /game/some-module/some-level, in this case, how do you pass the module name and level name in the Route
It looks like the Level route should be nested inside the Module component.
Here's a similar discussion on Stack Overflow: Nested routes with react router v4
And here's a good blog post that explains how nested routes work in React Router v4: https://tylermcginnis.com/react-router-nested-routes/

react-router-redux redirect to absolute url

I'm migrating a react application and I'm trying to split it. Basically, I would like to redirect some client-side react routes to absolute urls (or relative, but at least go with a server roundtrip, where reverse proxying is done)
Note that
react-router 3.0.0
react-router-redux 4.0.7
The app have these urls
http://myhost/ => homepage
http://myhost/someroute1 => a first route
http://myhost/someroute2 => a second route
http://myhost/someroute3 => a third route
Everything is inside react right now.
Routing looks like this :
<Provider store={store}>
<Router history={history}>
<Route path="/" component={Root}>
<IndexRoute component={Home} />
<Route path="/someroute1" component={Route1} />
<Route path="/someroute2" component={Route2} />
<Route path="/someroute3" component={Route3} />
</Route>
</Router>
</Provider>
The goal is to redirect, say, routes "/" and "/someroute2" to static urls (server urls). As so :
http://myhost/ => http://anotherhost/
http://myhost/someroute1 => keep react routing
http://myhost/someroute2 => http://anotherhost/anotherroute5
http://myhost/someroute3 => keep react routing
The question is simple : is is possible to replace, in a clean way, a react router route with an absolute url ?
I heard about Redirect and IndexRedirect components, but I can't figure how to use it properly, and, due to a lack of react / react-router, I can't figure if there would be any dangerous side-effects (in history for example).
Use Route's render prop instead of component. That way, you can specify a function to be called instead of a component to be instantiated. In the function, perform the navigation the old-fashioned way, using window.location.href:
<Route
path="/someroute2"
render={() => {
window.location.href = "http://anotherhost/anotherroute5";
return null;
}}
/>
Partially based on #brub answer, I've found a solution using a dumb component.
import React, { Component } from 'react'
export default class MyRedirectRoute extends Component {
componentDidMount() {
window.location.href = //my url here
}
render() {
return null
}
}
That I pass like this
<Route path="/someroute3" component={MyRedirectRoute} />
Though, I'm still not aware of a few things :
Is this a recommended solution ?
Are there any history side-effect ?
Is there any better (more react-router) solution than window.location.href ? I tried this.context.history without any success...
I'm waiting for feedback / better solution before accepting my own answer
You probably don't need React Router for this. The creator of React Router suggests using the <a> tag.
I haven't tried it but syntactically you could do it like this:
<Route
path="/someroute2"
render={() => <Redirect to="http://anotherhost/anotherroute5" />}
/>

React show information from one component to another that don't have common parant

I have the following react router:
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={MyHeader}>
<IndexRoute component={Main}/>
<Route path="carwash" component={CarWashPage} />
<Route path="carwashAdd" component={AddCarWashPage} />
<Route path="carwashAdd/:carWashId" component={EditCarWashPage} />
</Route>
</Router>,
destination
);
In component AddCarWashPage I have a form for carWash and when it is submitted I make redirection (browtherHistory.push('/carwash')) to component CarWashPage.
Now afte redirection I want to show in component CarWashPage information that carwash was save succesfully. How can I pass this text or maybe some flag from AddCarWashPage to CarWashPage that CarWashPage may to know when the correcsponding text has to be shown ?
The simple solution is Redux. If you're not looking to add this library, then you need to have either a global state bus (could get messy) or a common ancestor. Both of those routes are children to MyHeader. If you'd like to use the common ancestor approach then you could have MyHeader pass down a callback to the child.
Redux is still the way to go though so I highly recommend it.

How to use React Router with Electron?

Using this boilerplate as reference I created an Electron app. It uses webpack to bundle the scripts and express server to host it.
Webpack config is practically same as this and server this.
Electron's script loads:
mainWindow.loadURL('file://' + __dirname + '/app/index.html');
And index.html loads the script hosted by the server:
<script src="http://localhost:3000/dist/bundle.js"></script>
I run electron index.js to build the app and node server to start server which using webpack bundles the scripts.
It works fine, my React component App is mounted. But how I integrate react-router into this?
I implemented it the same way I would in a browser app. I get this error:
[react-router] Location "/Users/arjun/Documents/Github/electron-app/app/index.html" did not match any routes
It is taking file path as the route. Going through the boiler plate code did not help. What am I missing?
Had to Replace BrowserRouter with HashRouter.
import {
HashRouter,
Route
} from "react-router-dom";
And then in my index.js or the entry file of the Electron app I had something like this:
<HashRouter>
<div>
<Route path="/" exact component={ Home } />
<Route path="/firstPage" component={ FirstPage } />
<Route path="/secondPage" component={ SecondPage } />
</div>
</HashRouter>
And then everything just worked.
The reasoning: BrowserRouter is meant for request-based environments whereas HashRouter is meant for file-based environments.
Read more here:
https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/HashRouter.md
Another option would be to use hashHistory instead. Actually, in your referenced repo you can see that they're using hashHistory, how about trying that and posting back?
I'm using React Router v4 and didn't want to fallback to the HashRouter, so I solved it with something amongst the likes of:
import { Redirect, BrowserRouter } from 'react-router-dom';
const App = () => (
<BrowserRouter>
<div>
{window.location.pathname.includes('index.html') && <Redirect to="/" />}
</div>
</BrowserRouter>
);
Best option at the time of this answer is to use the MemoryRouter, worked for me :)
What about simply using Switch to default to "/" as follows:
<Switch>
<Route path="/" exact component={Home}/>
<Route path="/foo" component={Foo}/>
<Route path="/bar" component={Bar}/>
<Route render={() => <Redirect to="/"/>}/>
</Switch>
This way, "/index.html" will redirect to "/"
The (current) react-router docs say:
Generally speaking, you should use a <BrowserRouter> if you have a server that responds to requests and a <HashRouter> if you are using a static file server.
An Electron app is basically a static file server.
MemoryRouter can also work, so long as all routing originates from within the React part of the app. It only falls down when you want to navigate to a specific page from the Browser process, e.g. you want to pop up a new window and navigate directly to a "General Preferences" page. In that case, you can do this with HashRouter:
prefsWindow.loadURL(`file://${__dirname}/app/index.html#/general-prefs`);
I don't think there is a way to do that with MemoryRouter (from the Browser process).
Agree with Niekert.
But I believe it is better to handle like this before any route management.
if ( window.location.pathname.includes('index.html') ) {
location.pathname = ROUTES.ROOT;
}
It all depends on what kind of URL you pass to mainWindow.loadURL.
file://...
If you load index.html directly through the file:// protocol, such as
mainWindow.loadURL('file://' + path.join(__dirname, '../index.html#/home'))
then you need to use HashRouter:
<HashRouter>
<Routes>
<Route path='/home' element={<MyHomeScreen/>}>
</Routes>
</HashRouter>
Note that the # in index.html#/home is very important!
Why?
Think about what would happen if you wrote index.html/home. Your computer would try to retrieve a home file inside an index.html directory. The # prevents this, and thus we need to use HashRouter.
http://localhost:3000
If you load index.html from a server such as localhost:3000, then you have two options:
include the #, as in
mainWindow.loadURL('http://localhost:3000#/home')
and use HashRouter exactly as above,
OR
don't include the #, as in
mainWindow.loadURL('http://localhost:3000/home')
and use BrowserRouter like this:
<BrowserRouter>
<Routes>
<Route path='/home' element={<MyHomeScreen/>}>
</Routes>
</BrowserRouter>
Many people prefer to use BrowserRouter in this case because it avoids complicating the URL with a #.
In your main process:
mainWindow.loadURL(resolveHtmlPath('index.html'));
In your renderer process:
import { HashRouter as Router, Routes, Route } from 'react-router-dom';
//...
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/search" element={<Search />} />
<Route
path="/library"
element={<Library />}
/>
</Routes>
</Router>
The call to resolveHtmlPath removed the need for using hashtags for me. Using a BrowserRouter out of the box gave me the warning message in dev tools.
This function is in the Electron React Boilerplate project that you referenced:
import { URL } from 'url';
import path from 'path';
export function resolveHtmlPath(htmlFileName: string) {
if (process.env.NODE_ENV === 'development') {
const port = process.env.PORT || 1212;
const url = new URL(`http://localhost:${port}`);
url.pathname = htmlFileName;
return url.href;
}
return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`;
}

Categories

Resources