Mobx-state-tree: Using mobx-react-router with mobx-state-tree

Created on 16 Feb 2018  路  16Comments  路  Source: mobxjs/mobx-state-tree

I opened an issue in that library already but I thought it can be more interesting to have it here.
Is it possible in your opinion to use mobx-react-router.
What I need is basically change route programmatically inside the mst actions using react-router.

What I'm doing (or trying to do) is:

import createBrowserHistory from 'history/createBrowserHistory';
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router';
import Store from 'stores'

const browserHistory = createBrowserHistory();
const routingStore = new RouterStore(); // from mobx-react-router

const store = Store.create({ router: routingStore }) // Store is my main store in mst

const history = syncHistoryWithStore(browserHistory, routingStore); // from mobx-react-router

....

My main store is something like this:

const Store = types
  .model('Store', {
    auth: types.optional(AuthStore, {}),
    user: types.optional(UserModel, {}),
    router: types.optional(types.frozen)
  })

But then the syncHistoryWithStore is causing me an error Cannot assign to read only property 'history' of object '#<RouterStore>' because I guess the types.frozen don't accept mutability, am I correct?
Is there a work around, a better way / better type for this?

Most helpful comment

For anyone looking, I have now released an mst-react-router package, which hopefully should solve this problem.

All 16 comments

What about if you moved the store creation to after the syncHistoryWithStore line?
Does it still complain about immutability?

I just tried this briefly but it looks like is working! I'll keep this updated!
EDIT: now I got this error, not 100% sure is relevant but I think so (I think mst doesn't like the return type of the normal mobx @action)
EDIT 2: The problem here was that I was doing yield of store.push() that it wasn't returning anything, it was probably working as it is but I'm working on a mst friendlier version if you are interested

Uncaught (in promise) Error: [mobx-state-tree] Only promises can be yielded to `async`, got: [object Object]

It happen when .push method is called in a mst action, I'm trying to translate your code in a mst friendly one, let's see if I can get somewhere

@dbertella didn't fully get the status out of the conversation, can this issue be closed?

Hi sorry I was distracted from many other things, the update is, it's working as it is as far as I know, so I guess we can close this issue. I was trying to rewrite mobx-react-router in a more mst way but for some reason I couldn't achieve the same functionality so I need to try again soon, might need to learn more about mobx and mobx-state-tree first. I'll keep you updated if I came up with a better solution.

Confirmed, it's working.

Just

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

const routingStore = new RouterStore();
const browserHistory = createHistory();
export const history = syncHistoryWithStore(browserHistory, routingStore);
export default routingStore;
// store.js
import Router from './router';

const Model = types.model({
  router: types.optional(types.frozen),
});
export const store = Model.create({ router: Router });

@linonetwo if you change route with a Link from react-router is it updating the reference on the mst store or it "just" happen when using the internal store.router.push method?

@dbertella Both. To use <Link>, I also use @withRouter from react-router-dom.

I'm missing something because I can change route without a problem but onSnapshot doesn't get call and I don't have a new snapshot

@alisd23 I ended up writing my own implementation of mobx-react-router in mst, this is basically it.
I removed subscribe and unsubscribe methods because returning {...history} was causing problems. The Route were not updating for some reason. (I even tryed wrapping them in a withRouter Hoc but nothing changed)

This version seems to work perfectly for me

// RouterStore
import { types } from 'mobx-state-tree'

const Location = types.model('Location', {
  key: '',
  pathname: '',
  search: '',
  hash: '',
  state: types.frozen,
})

export const syncHistoryWithStore = (history, store) => {
  store._updateHistory(history)

  // Handle update from history object
  const handleLocationChange = location => {
    store._updateLocation(location)
  }

  history.listen(handleLocationChange)
  handleLocationChange(history.location)

  return history
}

export const RouterStore = types
  .model('RouterStore', {
    location: types.optional(Location, {
      key: '',
      pathname: '',
      search: '',
      hash: '',
      state: {},
    }),
  })
  .actions(self => {
    let history
    return {
      _updateLocation(newState) {
        self.location = newState
      },
      _updateHistory(initialHistory) {
        history = initialHistory
      },
      push(location) {
        history.push(location)
      },
      replace(location) {
        history.replace(location)
      },
      go(n) {
        history.go(n)
      },
      goBack() {
        history.goBack()
      },
      goForward() {
        history.goForward()
      },
    }
  })

// routes.js
import createBrowserHistory from 'history/createBrowserHistory'
import { RouterStore, syncHistoryWithStore } from 'stores/RouterStore'

const browserHistory = createBrowserHistory()
const routingStore = RouterStore.create({})

export const history = syncHistoryWithStore(browserHistory, routingStore)
export default routingStore
// index.js
const store = Store.create({ router: routingStore })

Hmm the { ...history } code was removed from mobx-react-router in version 4.0.1. You must be using an old version. Could you try upgrading then see if it works out of the box?

It doesn't update the store in my app even with the latest version.

I create a simple sandbox with mobx-react-router: https://codesandbox.io/s/l4mn1o12jm that should log the snapshot in the console and it doesn't seem to work. @linonetwo can you check if I made any stupid mistake?

This version instead https://codesandbox.io/s/913n1q60xw (with the mst transpiled version of mobx-react-router) works as expected

I tried wiring up mobx-react-router and mobx-state-tree as well, the routing works but it doesn't trigger the onSnapshot for me either, not even when calling router.push().

Basically the problem is that this line is updating only a property of a t.frozen object and not the object itself.
A solution would be to update the entire object like the example in #161, or to create a real mobx-state-tree branch like in the solution proposed from @dbertella

For anyone looking, I have now released an mst-react-router package, which hopefully should solve this problem.

Hey, faced this problem just this morning and by using @alisd23 package everything works a breeze! Thank you very much.

@alisd23 I started to get this typescript error that mst renamed property Snapshot. What was it renamed to? https://github.com/alisd23/mst-react-router/issues/10

Was this page helpful?
0 / 5 - 0 ratings

Related issues

A-gambit picture A-gambit  路  3Comments

xgenvn picture xgenvn  路  3Comments

LukaszAmroz picture LukaszAmroz  路  3Comments

donatoaz picture donatoaz  路  3Comments

tahv0 picture tahv0  路  3Comments