Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using MemoryRouter inside of BrowserRouter

In my project, I have to navigate from one component to other without changing the url. For this, I used MemoryRouter which worked as expected. But now the same idea should work for different routes i.e /one and /two. For example,

/one --> BrowserRouter (url change visible)
    /
    /first
    /second
    /third
/two --> BrowserRouter (url change visible)
    /
    /first
    /second
    /third

For the new visible routes, i.e /one and /two, the already established working Memoryroutes (i.e /, /first, /second, /third) should work properly with respective data as per provided in /one and /two.

I am struggling to include the MemoryRoutes inside the BrowserRoutes with the following structure code:

    <BrowserRouter>
      <Switch>
        <Route path="/one" render={() => <MemoryComp configFor="Dave"></MemoryComp>}></Route>
        <Route path="/two" render={() => <MemoryComp configFor="Mike"></MemoryComp>}></Route>
      </Switch>
    </BrowserRouter>

and then the MemoryComp holds:

  <MemoryRouter history={customHistory}>
    <Switch>
      <Route path="/" render={(props) => <InitPage configFor={this.props.configFor} history={props.history}></InitPage>}></Route>
      <Route path="/first" component={FirstPage}></Route>
      <Route path="/second" component={SecondPage}></Route>
      <Route path="/third" component={ThirdPage}></Route>
    </Switch>
  </MemoryRouter>

What I am trying to achieve:

  • To make the screens work with BrowserRouter --> MemoryRouter configuration.
  • To pass data from memory route to another memory route based on the main browser route. (Trying to use history to achieve the same)

Note: This is more likely better to handle the browser routing stuff with server routing instead. Also, this seems can be achieved with any react-stepper plugin. But trying to understand what I am doing wrong here, just for learning purpose.

Here is the whole minimized code, available on Stackblitz (not working):

import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
import { BrowserRouter, MemoryRouter, Route, Switch } from 'react-router-dom';
import { createBrowserHistory } from "history";

const customHistory = createBrowserHistory();

class MemoryComp extends Component{
  render(){
    return(
      <MemoryRouter history={customHistory}>
        <Switch>
          <Route path="/" render={(props) => <InitPage configFor={this.props.configFor} history={props.history}></InitPage>}></Route>
          <Route path="/first" component={FirstPage}></Route>
          <Route path="/second" component={SecondPage}></Route>
          <Route path="/third" component={ThirdPage}></Route>
        </Switch>
      </MemoryRouter>
    );
  }
}

class InitPage extends Component{
  render(){
    return (
      <>
        <ul>
          <li onClick={() => this.navigateTo("/")}>Init</li>
          <li onClick={() => this.navigateTo("/first")}>First</li>
          <li onClick={() => this.navigateTo("/second")}>Second</li>
          <li onClick={() => this.navigateTo("/third")}>Third</li>
        </ul>
        <div>{this.props.configFor}</div>
      </>
    )
  }

  navigateTo(path){
    this.props.history.push(path, {
      data: {
        configFor: this.props.configFor
      }
    })
  }
}

class FirstPage extends Component{
  constructor(props){
    super(props);
    this.data = this.props.history.location.state.data;
  }
  render(){
    return (
      <>
        <ul>
          <li onClick={() => this.navigateTo("/")}>Init</li>
          <li onClick={() => this.navigateTo("/first")}>First</li>
          <li onClick={() => this.navigateTo("/second")}>Second</li>
          <li onClick={() => this.navigateTo("/third")}>Third</li>
        </ul>
        <div>first page</div>
      </>
    )
  }

  navigateTo(path){
    this.props.history.push(path, {
      data: {...this.data, ...{pageName: 'first'}}
    })
  }
}

class SecondPage extends Component{
  constructor(props){
    super(props);
    this.data = this.props.history.location.state.data;
  }
  render(){
    return (
      <>
        <ul>
          <li onClick={() => this.navigateTo("/")}>Init</li>
          <li onClick={() => this.navigateTo("/first")}>First</li>
          <li onClick={() => this.navigateTo("/second")}>Second</li>
          <li onClick={() => this.navigateTo("/third")}>Third</li>
        </ul>
        <div>second page</div>
      </>
    )
  }

  navigateTo(path){
    this.props.history.push(path, {
      data: {...this.data, ...{name: 'deducedValue'}}
    })
  }
}

class ThirdPage extends Component{
  constructor(props){
    super(props);
    this.data = this.props.history.location.state.data;
  }
  render(){
    return (
      <>
        <ul>
          <li onClick={() => this.navigateTo("/")}>Init</li>
          <li onClick={() => this.navigateTo("/first")}>First</li>
          <li onClick={() => this.navigateTo("/second")}>Second</li>
          <li onClick={() => this.navigateTo("/third")}>Third</li>
        </ul>
        <div>third page</div>
      </>
    )
  }

  navigateTo(path){
    this.props.history.push(path, {
      data: this.data
    })
  }
}

class App extends Component {
  constructor() {
    super();
    this.state = {
      name: 'React'
    };
  }

  render() {
    return (
      <div>
        <BrowserRouter>
          <Switch>
            <Route path="/one" render={() => <MemoryComp configFor="Dave"></MemoryComp>}></Route>
            <Route path="/two" render={() => <MemoryComp configFor="Mike"></MemoryComp>}></Route>
          </Switch>
        </BrowserRouter>
      </div>
    );
  }
}

render(<App />, document.getElementById('root'));
like image 871
Mr_Green Avatar asked Mar 17 '20 05:03

Mr_Green


People also ask

What is the difference between BrowserRouter and Router?

The main difference between the two is the way they store the URL and communicate with your web server. A <BrowserRouter> uses regular URL paths.

What is a MemoryRouter?

A <Router> that keeps the history of your “URL” in memory (does not read or write to the address bar). Useful in tests and non-browser environments like React Native.

Should I use HashRouter and BrowserRouter?

HashRouter: When we have small client side applications which doesn't need backend we can use HashRouter because when we use hashes in the URL/location bar browser doesn't make a server request. BrowserRouter: When we have big production-ready applications which serve backend, it is recommended to use <BrowserRouter> .

Where do I put BrowserRouter?

BrowserRouter is usually used in your topmost Parent component. Check this post on medium: medium.com/@pshrmn/… Oh yes, that works!

What is <memoryrouter>?

The <MemoryRouter> solution <MemoryRouter> is a <Router> that keeps the history of your “URL” in memory (does not read or write to the address bar). Useful in tests and non-browser environments like React Native.

What is the difference between browserrouter and Route?

BrowserRouter: BrowserRouter is a router implementation that uses the HTML5 history API (pushstate, replacestate, and popstate events) to keep your UI in sync with the URL. It is the parent component used to store all other components. Route: This is a new component introduced in v6 and an upgrade of the component.

How to make the screens work with browserrouter?

To make the screens work with BrowserRouter --> MemoryRouter configuration. To pass data from memory route to another memory route based on the main browser route. ( Trying to use history to achieve the same) Note: This is more likely better to handle the browser routing stuff with server routing instead.

What is browserrouter in HTML5?

BrowserRouter: BrowserRouter is the router implementation for HTML5 browsers (vs Native) Switch: Switch returns only the first matching route rather than all matching routes. Routes: Route is the conditionally shown component based on matching a path to a URL.


1 Answers

At first I thought there was something wrong with Switch in combination with MemoryRouter, but after some debugging I realized it's actually totally independent.

The problem you have is that your base memory route needs to be exact, otherwise all other routes will match that one and first ('/') will be returned. Just add exact to your base route.

<MemoryRouter>
    <Switch>
      <Route exact path="/" render={(props) => <InitPage configFor={this.props.configFor} history={props.history}></InitPage>}></Route>
      <Route path="/first" component={FirstPage}></Route>
      <Route path="/second" component={SecondPage}></Route>
      <Route path="/third" component={ThirdPage}></Route>
    </Switch>
  </MemoryRouter>

Be careful if you have even more nested routes to always add exact to the root one e.g. /first needs to be exact in order for this to work correctly:

<Route exact path="/first" component={FirstPage}></Route>
<Route path="/first/nested-1" component={SecondPage}></Route>
<Route path="/first/nested-2" component={ThirdPage}></Route>
like image 105
zhuber Avatar answered Oct 06 '22 11:10

zhuber