GithubHelp home page GithubHelp logo

mobx-react-router's Introduction

/!\ The NPM location of this repository has changed /!\

Use @ibm/mobx-react-router

mobx-react-router

Keep your MobX state in sync with react-router via a RouterStore.

Router location state is observable, so any references to it in MobX components will cause the component to re-render when the location changes.

Very much inspired by (and copied from) react-router-redux.

This branch (master) is for use with react-router v6. Please, check the branch v5 for react-router v5.

Installation

npm install --save @ibm/mobx-react-router

/!\ npm install --save mobx-react-router is now deprecated, use npm install --save @ibm/mobx-react-router

And if you haven't installed all the peer dependencies, you should probably do that now:

npm install --save mobx mobx-react react-router

Although, mobx v6 deprecated decorators, this library still requires to enable them. Below is an example of configuration using vite.js:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  target: 'es2015',
  plugins: [
    react({
      babel: {
        plugins: [
          ['@babel/plugin-proposal-decorators', { legacy: true }],
        ],
      },
    }),
  ],
});

For more details, please consult the documentation of mobx v6 on decorators.

Usage

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { createBrowserHistory } from 'history';
import { Provider } from 'mobx-react';
import { RouterStore, syncHistoryWithStore } from '@ibm/mobx-react-router';
import { Router } from 'react-router';
import App from './App';

const browserHistory = createBrowserHistory();
const routingStore = new RouterStore();

const stores = {
  // Key can be whatever you want
  routing: routingStore,
  // ...other stores
};

const history = syncHistoryWithStore(browserHistory, routingStore);

ReactDOM.render(
  <Provider {...stores}>
    <Router location={routingStore.location} navigator={history}>
      <App />
    </Router>
  </Provider>,
  document.getElementById('root')
);

App.js

import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';

@inject('routing')
@observer
export default class App extends Component {
  render() {
    const { location, push, back } = this.props.routing;

    return (
      <div>
        <span>Current pathname: {location.pathname}</span>
        <button onClick={() => push('/test')}>Change url</button>
        <button onClick={() => back()}>Go Back</button>
      </div>
    );
  }
}

Check our live example.

HashRouter

You can replace history/createBrowserHistory with history/createHashHistory in the example above to use hash routes instead of HTML5 routing.

Troubleshooting

Routes not updating correctly when URL changes

There is a known issue with React Router 4 and MobX (and Redux) where "blocker" components like those created by @observer (and @connect in Redux) block react router updates from propagating down the component tree.

To fix problems like this, try wrapping components which are being "blocked" with React Router's withRouter higher order component should help, depending on the case.

API

RouterStore

const store = new RouterStore();

A router store instance has the following properties:

And the following history methods:

  • push(path)
  • replace(path)
  • go(n)
  • back()
  • forward()

syncHistoryWithStore(history, store)

  • history - A variant of a history object, usually browserHistory
  • store - An instance of RouterStore

returns an enhanced history object with the following additional methods:

  • subscribe(listener)
    Subscribes to any changes in the store's location observable
    Returns an unsubscribe function which destroys the listener
const unsubscribeFromStore = history.subscribe((location, action) => console.log(location.pathname));

history.push('/test1');
unsubscribeFromStore();
history.push('/test2');

// Logs
// 'test1'
  • unsubscribe()
    Un-syncs the store from the history. The store will no longer update when the history changes
history.unsubscribe();
// Store no longer updates

mobx-react-router's People

Contributors

alisd23 avatar danawoodman avatar dependabot[bot] avatar fiws avatar gcattan avatar hoppula avatar iamdanthedev avatar rokoroku avatar wrathza avatar zibra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mobx-react-router's Issues

Navigate from imported store?

Using mobx-react-router, would I be able to navigate from a mobx store (not just a react component) that is aware of the route history/location by importing it and using? Something like:

import { NavigationStore } from 'navigation-store';

class ContactStore {
  @action contactForm(name, message){
    // Some logic runs here

    // Then navigate back or success

    // Go back
    NavigationStore.goBack();

    // Or Navigate to Route
    NavigationStore.push('/success'); 
  }
}

I really need to be able to use react-router the same way (import the NavigationStore and call the store's available methods to navigate) since I have it working with react-navigation with RN, but trying to reuse a web version of the store with mobx-react-router for web.

how to define other store?

  const stores = {
    // Key can be whatever you want
    routing: routingStore,
    // ...other stores
  };

this code . but how to define other stores? has any doc. thank you

No use with react-router v4

When I click the <Link> to redirect, the url in browser correctly change but nothing happen in the page.

Here is my code:

// Root.tsx
import * as React from 'react'
import createBrowserHistory from 'history/createBrowserHistory'
import {Link, Route, Switch} from 'react-router-dom'
import {Provider} from 'mobx-react'
import {Router} from 'react-router'
import {syncHistoryWithStore, RouterStore} from 'mobx-react-router'

const routing = new RouterStore()

const browserHistory = createBrowserHistory()

const history = syncHistoryWithStore(browserHistory, routing)

function PageHome () {
  return (
    <div>
      <h1>home</h1>
      <Link to='/404'>404</Link>
    </div>
  )
}

function Page404 () {
  return (
    <div>
      <h1>404</h1>
      <Link to='/'>home</Link>
    </div>
  )
}

export default class Root extends React.Component<void, void> {
  render () {
    return (
      <Provider routing={routing}>
        <Router history={history}>
          <Switch>
            <Route path='/' exact component={PageHome}/>
            <Route component={Page404}/>
          </Switch>
        </Router>
      </Provider>
    )
  }
}
// index.tsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import Root from './Root'
import {AppContainer} from 'react-hot-loader'
import './styles/base.styl'

const render = (Root: any) => {
  ReactDOM.render(
    <AppContainer>
      <Root/>
    </AppContainer>,
    document.getElementById('app')
  )
}

render(Root)

if (module.hot) {
  module.hot.accept('./Root', () => {
    const nextRoot = require('./Root').default
    render(nextRoot)
  })
}

Nested routes

I can't seem to get nested routes working as explained here: https://reacttraining.com/react-router/core/guides/philosophy/nested-routes

mobx-react-router is a bit different from vanilla react-router so I'm using location.pathname instead of match.url. To be honest, I'm not sure whether this is react-router issue or mobx-react-router issue so if you have a working nested route setup with mobx-react-router, please do share :)

update peer dependencies

When using with mobx@^5.0.0 npm gives following warning:

npm WARN [email protected] requires a peer of mobx@^3.0.0 || ^4.0.0 but none is installed. You must install peer dependencies yourself.

Can the peer dependencies of mobx-react-router in the package.json file be updated to reflect its compatibility with mobx 5?

Accessing history outside of component

How to access ( push) history outside of component, e.g. when without using .props.
I tried to access via routing store e.g.

stores.routing.history.push('/dashboard/');

That doesnt worked. Accessing object created from createBrowserHistory doesnt work too.
So how to properly push to history?

mobx 4 support

There's a warning about incorrect peer dependency when used with mobx 4, but it otherwise works. Maybe something like this?

diff --git package.json package.json
index 47f8060..0f6e801 100644
--- package.json
+++ package.json
@@ -59,7 +59,7 @@
     "eslint-plugin-react": "^6.3.0",
     "eslint-plugin-standard": "^2.0.0",
     "jest": "^19.0.0",
-    "mobx": "^3.0.2",
+    "mobx": "^4.0.0",
     "mobx-react": "^4.1.0",
     "react": "^15.0.0",
     "react-addons-test-utils": "^15.3.2",
@@ -72,7 +72,7 @@
     "webpack": "^2.3.2"
   },
   "peerDependencies": {
-    "mobx": "^3.0.0",
+    "mobx": "^3.0.0 || ^4.0.0",
     "react-router": "4.x"
   }
 }

.. with Typescript Property 'routing' does not exist on type 'Readonly<{ children?: ReactNode; }> & Readonly<{}>

I've installed "@types/history": "^4.6.2","@types/jest": "^23.3.0", "@types/node": "^10.5.2","@types/react": "^16.4.6", "@types/react-dom": "^16.0.6", "@types/react-router": "^4.0.29", but I still get that error

import * as React from 'react';
import { inject, observer } from 'mobx-react';

@inject('routing')
@observer
export default class App extends React.Component {
  render() {
    const { location, push, goBack } = this.props.routing; // here

    return (
      <div>
        <span>Current pathname: {location.pathname}</span>
        <button onClick={() => push('/test')}>Change url</button>
        <button onClick={() => goBack()}>Go Back</button>
      </div>
    );
  }
}
Property 'routing' does not exist on type 'Readonly<{ children?: ReactNode; }> & Readonly<{}>

Uncaught TypeError: history.getCurrentLocation is not a function at syncHistoryWithStore

I have spent a couple of days trying to work around all the various issues with Typings and versions for history, react-router, mobx-react-router trying to find a combination that works... without success.

I believe I have the Typescript issues sorted, but I have the following error message during execution:

Uncaught TypeError: history.getCurrentLocation is not a function
    at syncHistoryWithStore (main-client.js:8204)

Which occurs at the following, with this line and the imports copied and pasted from the example:

const history = syncHistoryWithStore(browserHistory, routingStore);

Everything else works fine if I comment out that line, and just use the same browserHistory in the Router.

I have tried just about every combination of versions. My current package.json is as follows:

{
  "name": "NetCoreReactMobX",
  "version": "0.0.0",
  "license": "MIT",
  "dependencies": {
    "@types/history": "^4.5.0",
    "@types/react": "^15.0.14",
    "@types/react-dom": "^0.14.14",
    "@types/webpack": "^2.2.0",
    "@types/webpack-env": "^1.13.0",
    "aspnet-prerendering": "^2.0.0",
    "aspnet-webpack": "^1.0.27",
    "aspnet-webpack-react": "^1.0.4",
    "awesome-typescript-loader": "^3.0.0",
    "babel-core": "^6.5.2",
    "babel-loader": "^6.2.3",
    "babel-plugin-mobx-deep-action": "^1.5.2",
    "babel-preset-es2015": "^6.5.0",
    "babel-preset-react": "^6.5.0",
    "bootstrap": "^3.3.6",
    "css-loader": "^0.26.2",
    "domain-task": "^3.0.0",
    "event-source-polyfill": "^0.0.9",
    "extract-text-webpack-plugin": "^2.0.0-rc",
    "file-loader": "^0.10.1",
    "history": "3.2",
    "jquery": "^2.2.1",
    "json-loader": "^0.5.4",
    "mobx": "^3.1.2",
    "mobx-react": "^4.1.1",
    "mobx-react-router": "^3.1.2",
    "node-noop": "^1.0.0",
    "react": "^15.3.2",
    "react-dom": "^15.3.2",
    "react-router": "^3.0.2",
    "style-loader": "^0.13.0",
    "typescript": "^2.2.1",
    "url-loader": "^0.5.7",
    "webpack": "^2.2.1",
    "webpack-hot-middleware": "^2.12.2",
    "webpack-merge": "^3.0.0"
  },
  "devDependencies": {
    "babel-plugin-transform-class-properties": "^6.23.0",
    "babel-plugin-transform-decorators-legacy": "^1.3.4",
    "babel-preset-stage-2": "^6.22.0"
  }
}

And my tsconfig.json is as follows (note I use TypeScript to ES6, then Babel to ES5)

{
  "compilerOptions": {
    "baseUrl": ".",
    "moduleResolution": "node",
    "target": "es6",
    "jsx": "preserve",
    "experimentalDecorators": true,
    "sourceMap": true,
    "allowJs": false,
    "skipDefaultLibCheck": true,
    "lib": ["es6", "dom"],
    "types": [ "webpack-env" ],
    "paths": {
      // Fix "Duplicate identifier" errors caused by multiple dependencies fetching their own copies of type definitions.
      // We tell TypeScript which type definitions module to treat as the canonical one (instead of combining all of them).
      "history": ["./node_modules/@types/history/index"],
      "react": ["./node_modules/@types/react/index"]
    }
  },
  "awesomeTypescriptLoaderOptions": {
    "silent": true,
    "useBabel": true,
    "useCache": false
  },
  "exclude": [
      "bin",
      "node_modules"
  ]
}

Any pointers would be really appreciated...

Also, is there a possibility of a sample project, that can be used as a reference point with compatible versions of mobx-react-router, history and react-router? It was a nightmare to get rid of TypeScript warnings.

will v3 with latest mobx?

Does anyone know if v3 of this package can be made to work with latest mobx? When I upgrade mobx, I get store.location undefined

Support params

Currently there is only location has transformed into observable object and that's great. But why didn't you do the same for params? Are there any concerns that I might have missed out?

With TypeScript: Property 'history' does not exist on type 'Router'

In your usage example you're passing a history prop to the <Router> component. Typescript doesn't like that because it doesn't have such prop defined.

I know we can extend the interface to include the prop but I was wondering why do you need to send a prop to the Router component if react-router-dom won't actually use it. Or is it that it's propagated down in the tree and that's what you need?

Anyway, consider adding a d.ts file extending the Router interface to allow a history prop.

n element descriptor's .kind property must be either “method” or “field”

I'm following the documentation for mobx-react-router but upon attempting to run my application I get the following error in the browser:

Uncaught TypeError: An element descriptor's .kind property must be either "method" or "field", but a decorator created an element descriptor with .kind "undefined"
    at _toElementDescriptor (app.js:49988)
    at _toElementFinisherExtras (app.js:49990)
    at _decorateElement (app.js:49980)
    at app.js:49976
    at Array.forEach (<anonymous>)
    at _decorateClass (app.js:49976)
    at _decorate (app.js:49958)
    at Module../src/App/UserStore.js (app.js:50012)
    at __webpack_require__ (bootstrap:19)
    at Module../src/index.js (index.js:1)

Here is how I intitialize:

const appContainer = document.getElementById('app');
if(appContainer) {
  const browserHistory = createBrowserHistory()
  const routingStore = new RouterStore();

  const stores = {
    users: userStore,
    routing: routingStore
  }

  const history = syncHistoryWithStore(browserHistory, routingStore);

  ReactDOM.render(
    (
      <Provider {...stores}>
        <Router history={history}>
          < App />
        </Router>
      </Provider>
    ),
  appContainer);
}

And this is how I use:

@inject('routing')
@inject('users')
@observer
class App extends Component { ...

My UserStore:

import { observable, action, computed } from "mobx"

class UserStore {
  @observable users = [];

  @action addUser = (user) => {
    this.users.push(user)
  }

  @computed get userCount () {
    return this.users.length
  }
}

const store = new UserStore();
export default store;

I've tried to Google for this error but it's returning no useful results. Any ideas what I am doing wrong?

Update Peer Dependency

react-router now at 5.0.0. Can the peer-dependencies in package.json be updated?

"peerDependencies": {
"mobx": "^3.0.0 || ^4.0.0 || ^5.0.0",
"react-router": "4.x" <-----
}

Looking for maintainers

At the moment I just don't have any time to manage this repository, and I don't use it regularly anymore.

As this project has a reasonable amount of usage, I'm looking for maintainers to prevent it from dying. I've not been paying attention for the last few months (sorry).

I'm happy to add new collaborators (with full control) or if someone really wants (given they'll actually maintain it) I'm happy to transfer ownership.

If you're interested, drop a comment on this issue, or just email me at [email protected]! 👍

Router Context is Swallowed

I am here to resurrect #19 and #16, for which I apologize.

I believe the current state of things is that mobx (and mobx-react-router) don't really work out of the box with react-router v4.

Cause:
observer components use a customized shouldComponentUpdate that pays attention to the mobx context but not the context that react-router needs.

Here's a repro:

Current workaround:
Users of this lib should wrap withRouter around every observer in their app.

Solutions:
Though this isn't mobx-react-router's fault (remix-run/react-router#4781, etc, etc) it does appear that this library owns the space at the intersection of mobx and react-router and is therefore in the best position to bundle a fix or workaround.

Short-term solution: This should be called out prominently in the readme. It's relevant to ~100% of people who use this lib (I think?)

Longer-term solutions: A fix or workaround should be integrated into this lib if possible. I think it is possible! Anything is possible, right? Here are some potential approaches:

  1. Whatever react-router-redux does. Assuming it has this problem solved, which I haven't verified.
  2. As part of syncHistoryWithStore, somehow turn the history attributes that react-router uses into observables. If that's a thing.
  3. Add a function/decorator that takes the context react-router needs out of the routing store and puts it into the routing context of the child component. You'd still need to apply this decorator to anything that uses a Route or NavLink, but at least you don't have to wrap every single observer in withRouter.
  4. Provide a router-friendly alternative to observer as part of this lib. This at least provides a solution out of the box and gets people like me to stop writing our own dubious one-offs.

Happy to submit a PR for the README or to look into options 3/4 if you agree with the above.

Thanks!
Daniel

goBack doesn't update the Route components the first time

Hey, I was playing around with your example and added a couple of Route components just to test it, I noticed that calling goBack or pushing back from the browser updates the url but has no effect the first time on the Route components, why do you think that happens? I would help but have no idea.

Here is the example
https://mobx-react-router-test-wyyurqrtst.now.sh/

And here is the code
https://github.com/marcofugaro/mobx-react-router-test

An Example App?

Hi

I am wondering if there is some sample app? I followed the usage but I am confuse what "push(/test)" is. Is that another component or what?

New react-router typings causes mismatch with mobx-react-router

The issue

The typings for react-router was updated in March 2018, with this PR: DefinitelyTyped/DefinitelyTyped#24168

That has created a slight mismatch in typings for the history object in react-router and mobx-react-router. react-router now states that the history prop cannot be undefined / null, (which is correct, <Router> says that history is required). However the history returned from the RouterStore by mobx-react router has the possibility to be undefined. So now we need to check if history is defined or not every time we pass it to a <Router> component, which is not ideal.

Judging by the source code in sync.js it does really seem like the history property in RouterStore is undefined, until syncHistoryWithStore has been called.

Current workaround

I've created a RouterStoreSynchronized class that extends from RouterStore but explicitly sets the history and location properties to exist. I know they do, because I call syncHistoryWithStore immediately after creation.

export class RouterStoreSynchronized extends RouterStore {
	history!: History;
	location!: Location;
}

const browserHistory = createBrowserHistory();
export const routerStore = new RouterStoreSynchronized();
syncHistoryWithStore(browserHistory, routerStore);

Possible long-term solutions

  1. require all TypeScript users to do routerStore.history! every time, instead of just routerStore.history. This feels annoying to me.
  2. Make syncHistoryWithStore() return both SynchronizedHistory and a SynchronizedRouterStore, that we use instead of the default RouterStore. We always know that history and location are present after syncHistoryWithStore(), so we can safely return a SynchronizedRouterStore in that function, and make the user refer to that instance everywhere.
  3. Completely replace the current API with a single function, createRouterStore(), that takes a history object, and returns a pre-synchronized RouterStore, eliminating the need to run syncHistoryWithStore. At the moment, a RouterStore that hasn't been synced is useless anyway, so it seems a bit weird that we are required to do the two step process of instantiating a RouterStore and then synchronizing it. This new API could look something like:
const browserHistory = createBrowserHistory();
const routingStore = createRouterStore(browserHistory); //synchronization handled in function

const stores = {
  // Key can be whatever you want
  routing: routingStore,
  // ...other stores
};

ReactDOM.render(
  <Provider {...stores}>
    <Router history={routingStore.history}> // get history from RouterStore
      <App />
    </Router>
  </Provider>,
  document.getElementById('root')
);

Instead of a createRouterStore() function, it could just be part of the RouterStore constructor, ie. new RouterStore(browserHistory), it would result in the same.

What are your thoughts on all of this?
Let me know if you would like a PR with any of this.

Pass state with RoutingStore.push()

The main library (react-router v4) supports adding state to the history, different from parameters and the url):

https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/history.md

push(path, [state]) this can then be read through location.state

This is very useful for creating several tools: ie to have a "back" button that works on special keyframes. - the location state would describe where to go to instead of just "back".

I do not see an equivalent with mobx-react-router. Am I missing something?

How to get route params?

If we use @withRouter we will receive "match" in props.
How can we get match.params from routingStore ?

  • Except by parsing the location by own regexp

React Native?

Hello :)

Just wandering if this library would also work on react native or just web?

Any way to update a hash route?

Got the following issue:
{domain}{?someParams}#/{route}

That's my url, i need to update {?someParams} while preserving #/{route}.

Is there any elegant way for doing this?

Edit:
Right now i get:

{domain}{?someParams}#/{route}{?someParams}

when i try to replace it.

Issue with TypeScript when trying to use SynchronizedHistory from RouterStore

Consider I'm creating my Router like so:

import React from 'react';
import { Router } from 'react-router';
import { Provider } from 'mobx-react';
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router';
import createHashHistory from 'history/createHashHistory';

const hashHistory = createHashHistory();
const routingStore = new RouterStore();
const history = syncHistoryWithStore(hashHistory, routingStore);

const rootStore = { ...someStores, routingStore }

class Routers extends React.Component {
  render() {
    return (
      <Provider {...rootStore}>
        <Router history={history}>

And then in some component I want to inject the routingStore and use the SynchronizedHistory's subscribe method to do something, like so:

this.props.routingStore.history.subscribe(() => { ... });

Since routingStore is a RouterStore, it has a basic History, and not a SynchronizedHistory. When I try to use subscribe it obviously says Property 'subscribe' does not exist on type 'History<any>.

What am I doing wrong here? Is there a generic RouterStore type I'm missing with a History type that I can use to define routingStore with?

How the heck can I make this work?

I've ran out of ideas or things to test. I don't know what I am doing anymore. Please help! Can you please put an example in the docs of how to add a few routes? Thanks!

Index.jsx:

import React from 'react';
import ReactDOM from 'react-dom';
import createBrowserHistory from 'history/createBrowserHistory';
import { Provider } from 'mobx-react';
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router';
import { Router } from 'react-router';
import mainStore from './store';
import App from './components/app';
import './components/bundle.scss';

const browserHistory = createBrowserHistory();
const routingStore = new RouterStore();

const stores = {
  routing: routingStore,
  mainStore
};

const history = syncHistoryWithStore(browserHistory, routingStore);

ReactDOM.render(
  <Provider {...stores}>
    <Router history={history}>
      <App />
    </Router>
  </Provider>,
  document.getElementById('react-root')
);

app.jsx

import React, { Component } from 'react';
// import ReactGA from 'react-ga';
import Helmet from 'react-helmet';
import { inject } from 'mobx-react';
import { Route } from 'react-router-dom';
// import * as OfflinePluginRuntime from 'offline-plugin/runtime';
import injectTapEventPlugin from 'react-tap-event-plugin';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import Notification from './common/notification/notification';
import Loader from './common/loader/loader';
import Home from './home/home';
import PageNotFound from './pageNotFound/pageNotFound';

@inject('routing')
class App extends Component {

  componentDidMount() {
    // OfflinePluginRuntime.install();
    injectTapEventPlugin();
  }

  render() {
    // const title = this.props.routes[1].title
    //   ? `${this.props.routes[1].title} | AppName`
    //   : 'AppName';

    console.log(this.props);

    return (
      <section className="app-container">
        <Route path="/" component={Home} />
        <Route path="*" component={PageNotFound} />
        {/* <Helmet title={String(title)} /> */}
        <Loader />
        <Notification />
        <MuiThemeProvider>
          {React.cloneElement(this.props.children, this.props)}
        </MuiThemeProvider>
      </section>
    );
  }
}

export default App;

Error:

error

Support mobx-state-tree

Right now you can use mobx-react-router with mobx-state-tree treating the router as a generic types.frozen, it works fine but mobx-state-tree doesn't trigger an update when location changes, because it's a property of the frozen object. Here is an example of it.

A solution would be to treat the router as a real branch in MST, with a model and everything like demonstrated in this example.

Probably a RouterStore exported for MST would work, something like import { RouterStore } from 'mobx-react-router/mst'. Do you wish to support mobx-state-tree?

Examples use decorators, but decorators are not supported.

While i like the fact that the examples include decorators for future javascript versions, the examples are not useful for today. Decorators are not supported in any browser right now. It would be nice to have all examples in the README written in ECMAScript 5.

[question] this vs react-router-dom

Good job in this library, however, I wonder:

what would be the benefits in keeping the history synced in a store, as opposed to just wrap any component that needs it with the @withRouter HOC.

Server Side Rendering is broken in v4.0.6

v4.0.6 was built with globalObject set to window instead of this, so it doesn't work in Node anymore because window is not defined.
I would suggest considering removing webpack completely or switching to rollup, as webpack bootstrap is bigger than the library itself.

error history.getCurrentLocation is not a function

Hi :)

I've add mobx-react-router to my project, but i've this error when i instance my store :

Uncaught TypeError: history.getCurrentLocation is not a function

My store.js :

import createBrowserHistory from 'history/createBrowserHistory';
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router';
import app from './app';
import user from './user';

const browserHistory = createBrowserHistory();
const routingStore = new RouterStore();

const store = {
  app,
  user,
  routing: routingStore
};

const history = syncHistoryWithStore(browserHistory, routingStore);

export { history };
export default store;

And my App.js :

const App = (
  <Provider {...store}>
    <Router history={history}>
      <Route path="/" component={AppContainer} />
    </Router>
  </Provider>
);

I use react-router 4.1.2 :)

Anyone have idea about this error ?

Thank you community !

testing in react-create-app stack

Hi.
I'm using CRA, with mobx, react-router@4 and this project.

In the app itself everything goes well. I have routerStore like example, and I pass history prop to Router.
I need withRouter for every component that uses this functionality but that's fine.

The problem starts at testing,
I manage to change path from the app (with simulate click on some btn that do props.history.push('..') )
with
function assertCorrectPath(ev) { expect(ev).toEqual('/'); } MainWrapper.context().router.history.listen(assertCorrectPath);
I have it on context thanks to:
react-router-test-context.

But, I cannot be in sync with path when I change it from the test:
I do wrapper.context().router.history.push('cc');
and the Component is not rendered accordingly.

Is there any easier way for all that?

Back button not working?

Hi

Not sure if I am doing this right but I have something like this


const browserHistory = createBrowserHistory();
const history = syncHistoryWithStore(browserHistory, stores.routingStore);

ReactDOM.render(
    <Provider {... stores}>
        <Router history={history}>
             <MyComponent />
        </Router>

    </Provider>,      
       document.getElementById('root')
);

Then in MyComponent

 <button onClick={() => push('/component1')}>component1</button>
 <button onClick={() => push('/component2')}>component2</button>

 <Route exact path='/component1' component={Component1}/>
 <Route exact path='/component2' component={Component2}/>

The components load when I click the button but when I hit the "back button" in my browser the url changes but the component stays the same.

How to use with HashRouter?

I would like to use this with the HashRouter, otherwise I lose my location on a page refresh. But HashRouter doesn't not take a history property, so I don't think it is possible to set this up with mobx-react-router?

Using Browser buttons, gets rendered page out of sync with path

My routing seems to work fine. But when I use the browser 'back' button, the URL changes correctly, but the page then doesn't match the URL. The first time I hit the back button, nothing happens. Then second time I click it goes to where it should have gone the first time.

This is my entry point: index.tsx:

 {UserStore} from './data/stores/UserStore';
declare let module: any;

import * as React from "react";
import * as ReactDOM from "react-dom";
import {AppContainer} from 'react-hot-loader';
import { Router } from 'react-router';
// import createHistory from 'history/createBrowserHistory';
import createHistory from 'history/createHashHistory';
import { Provider } from 'mobx-react';
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router';
import { App } from "./components/app/App";

const history = createHistory();
const routingStore = new RouterStore();
const users = new UserStore();
const synchronizedHistory = syncHistoryWithStore(history, routingStore);
const stores = {
    routing: routingStore,
    users: users
};

const renderApp = (Component: any) =>
{
    ReactDOM.render(
        <AppContainer>
            <Provider {...stores}>
                <Router history={synchronizedHistory}>
                    <Component />
                </Router>
            </Provider>
        </AppContainer>,
        document.getElementById("app_content")
    );
};

renderApp(App);

// Hot Module Replacement API
if (module.hot) {
    module.hot.accept('./components/app/App', () => {
        renderApp(App)
    });
}

My app file:

import 'bootstrap';
import * as React from 'react';
import {TopNav} from '../topnav/TopNav';
import {History} from 'history';
import {Routes} from './Routes';
import {inject, observer} from 'mobx-react';
import {UserStore} from '../../data/stores/UserStore';
import {Level, Logger} from '../../util/Logger';

interface IAppProps { routing: History, users: UserStore }

@inject('routing')
@observer
export class App extends React.Component<IAppProps, undefined>
{
    //noinspection JSMethodCanBeStatic,JSUnusedLocalSymbols
    private log(msg: string, level?: Level): void
    {
        Logger.get().log(msg, 'App', level || Level.TRACE);
    }

    render(): JSX.Element
    {
        const { location } = this.props.routing;
        return (
            <div>
                <TopNav/>
                <div className='container body-content'>
                    <Routes/>
                    <div className='small'>path: {location.pathname}</div>
                </div>
            </div>);
    }
}

My routes file:

import * as React from 'react';
import {Route, Switch} from 'react-router-dom';
import {HomePage, PageOne, PageThree, PageTwo} from '../home/HomePage';
import {PendingDeliveryPage} from '../delivery/PendingDeliveryPage';
import {DeliveryExecutePage} from '../delivery/DeliveryExecutePage';


export class Routes extends React.Component<undefined, undefined>
{
    public render(): JSX.Element
    {
        return (
            <Switch>
                <Route exact path='/' component={HomePage} />
                <Route path='/delivery/:id/execute' component={DeliveryExecutePage} />
                <Route path='/delivery' component={PendingDeliveryPage} />
                <Route path='/one' component={PageOne} />
                <Route path='/two' component={PageTwo} />
                <Route path='/three' component={PageThree} />
            </Switch>
        );
    }
}

And lastely my test pages:

import * as React from "react";
import {Link, RouteComponentProps} from 'react-router-dom';

export class HomePage extends React.Component<RouteComponentProps<any>, undefined>
{
    public render(): JSX.Element
    {
        return (
            <div>
                <h1>Home</h1>
                <ul>
                    <li><Link to="/">Home</Link></li>
                    <li><Link to="/one">One</Link></li>
                    <li><Link to="/two">Two</Link></li>
                    <li><Link to="/three">Three</Link></li>
                </ul>
            </div>);
    }
}

export class PageOne extends React.Component<RouteComponentProps<any>, undefined>
{
    public render(): JSX.Element
    {
        return (
            <div>
                <h1>Page One</h1>
                <ul>
                    <li><Link to="/">Home</Link></li>
                    <li><Link to="/one">One</Link></li>
                    <li><Link to="/two">Two</Link></li>
                    <li><Link to="/three">Three</Link></li>
                </ul>
            </div>);
    }
}

export class PageTwo extends React.Component<RouteComponentProps<any>, undefined>
{
    public render(): JSX.Element
    {
        return (
            <div>
                <h1>Page Two</h1>
                <ul>
                    <li><Link to="/">Home</Link></li>
                    <li><Link to="/one">One</Link></li>
                    <li><Link to="/two">Two</Link></li>
                    <li><Link to="/three">Three</Link></li>
                </ul>
            </div>);
    }
}

export class PageThree extends React.Component<RouteComponentProps<any>, undefined>
{
    public render(): JSX.Element
    {
        return (
            <div>
                <h1>Page Three</h1>
                <ul>
                    <li><Link to="/">Home</Link></li>
                    <li><Link to="/one">One</Link></li>
                    <li><Link to="/two">Two</Link></li>
                    <li><Link to="/three">Three</Link></li>
                </ul>
            </div>);
    }
}

You can click

  1. Start at 'Home'
  2. Click 'One'
  3. Click 'Two'
  4. Click 'Three', and everthing should be fine.
  5. Click Browser Back, path changes to /two, but page does not render anything new.
  6. Click Browser Back and path changes to /one, and now page /two renders.
  7. Click Browser Back and page changes to /, but page /one renders.
  8. etc.

Am i doinng something wrong here?

TS : history definition error

Hi,

I use React with TS (I use react-scripts-ts and when I define the Router history, I've got this error :

Error in ./src/index.tsx
(32,15): error TS2322: Type 'SynchronizedHistory' is not assignable to type 'History | undefined'.
  Type 'SynchronizedHistory' is not assignable to type 'History'.
    Types of property 'listenBefore' are incompatible.
      Type '(hook: TransitionHook) => UnsubscribeCallback' is not assignable to type '(hook: TransitionHook) => () => void'.
        Types of parameters 'hook' and 'hook' are incompatible.
          Type 'History.History.TransitionHook' is not assignable to type 'MobxReactRouter.TransitionHook'.
            Types of parameters 'location' and 'location' are incompatible.
              Type 'MobxReactRouter.Location' is not assignable to type 'History.Location'.
                Property 'query' is optional in type 'Location' but required in type 'Location'.

It comes from this line : <Router history={history} >
I did exactly like the documentation : const history = syncHistoryWithStore(browserHistory, routingStore);

Thank you for your work !

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.