xaviergonz / mobx-keystone

A MobX powered state management solution based on data trees with first class support for Typescript, support for snapshots, patches and much more
https://mobx-keystone.js.org
MIT License
554 stars 25 forks source link

getRootStore returns undefined. #530

Closed AshwinDeTaeye closed 1 year ago

AshwinDeTaeye commented 1 year ago

I noticed a bug in my older app. I use :

import { getRootStore } from 'mobx-keystone';

const rootStore = getRootStore<RootStore>(this);

This used to work and i used it allot in my app, however, in a previous update of the app it only return undefined. This hasn't been noticed so i'm not sure if updating mobx-keystone in the past had something to do with it. I checked the changelogs and there seemed to be no breaking changes concerning the getRootStore.

I setup a store like this:

rootStore = new RootStore({
        ticketList: new TicketList({}),
        basket: new Basket({}),
      });

And where i call the getRootStore:


setGlobalConfig({
  modelAutoTypeChecking: ModelAutoTypeCheckingMode.AlwaysOn,
});
@model('keystone/Application/TicketList')
export default class TicketList extends Model({
  ticketModels: tProp(
    types.array(types.model<TicketModel>(TicketModel)),
    () => []
  ),
}) { 
onInit = () => {
    const rootStore = getRootStore<RootStore>(this);
    console.log('Should be the rootStore: ', rootStore);  // Gives undefined
}
}
"mobx": "^6.0.4",
"mobx-keystone": "^0.66.0",
"mobx-react": "^7.3.0",
xaviergonz commented 1 year ago

that should only return something after you call registerRootStore. where are you calling it?

AshwinDeTaeye commented 1 year ago

I call registerRootStore(rootStore); in my createRootStore function:


export async function createRootStore() {
  const rawStored = localStorage.getItem('newst');
  if (typeof rawStored === 'string') {
    try {
      const savedRootStore = await JSON.parse(rawStored);
      rootStore = fromSnapshot<RootStore>(savedRootStore);
      registerRootStore(rootStore);
    } catch (e) {
      console.log('There was an error restoring the initial localStorage');
    }
  } else {
    try {
      rootStore = new RootStore({
        ticketList: new TicketList({}),
        basket: new Basket({}),
      });
    } catch (e) {
      console.log('There was an error creating rootStore from scratch', e);
    }
    registerRootStore(rootStore);
  }

  return rootStore;
}

I call this function at the start of the app:

(async () => {
  const st = await createRootStore();
  return console.log('1. CREATED rootStore');
})();
AshwinDeTaeye commented 1 year ago

If i remove all the registerRootStore() from my code, i get the same result the app starts and works. On the home page, there is already some data from mobx keystone state. But when i do getRootStore(this) in another part of the tree, it doesn't give me the rootStore.

As a test, i have placed another registerRootStore() after where i create the rootStore. And then i get the error: object already registered as root store

So it looks like it has been registered successfully in the createRootStore function.

(async () => {
  const st = await createRootStore();
  registerRootStore(st);
  return console.log('1. CREATED rootStore');
})();

Above gives the error: object already registered as root store

xaviergonz commented 1 year ago

That's expected (and the behavior hasn't changed from the first versions):

// here no rootstore is registered

rootStore = new RootStore({
  ticketList: new TicketList({}), // here onInit is called, where no rootstore is still registered
});

// here is where the root store gets actually registered
registerRootStore(rootStore);

if you want to use the rootstore as part of a "sort of" initialization phase use onAttachedToRootStore instead

https://mobx-keystone.js.org/root-stores#onattachedtorootstore

that callback gets called whenever an object "enters/leaves" a rootstore, therefore in your case TicketList.onAttachedToRootStore will get called at the very same time that you call registerRootstore

AshwinDeTaeye commented 1 year ago

i had changed onAttachedToRootStore() wrongly to onInit() at some point. Thank you very much!