skip to Main Content

I create a Mobx State Tree global state composed by a DataModel that contains a TranslationsDataModel. In DataModel I created a fetchDataset function that I would like to call in TranslationsDataModel but I get an error saying that fetchDataset is not a function and I don’t understand why.

This is my code:

index.ts:

export const StateModel = types.model('StateModel', {
  data: types.optional(DataModel, {} as DataModelInstance),
})

export const stateInstance = StateModel.create()
export interface StateInstance extends Instance<typeof StateModel> {}
const RootStateContext = createContext<StateInstance | null>(null)
export const Provider = RootStateContext.Provider

export function useMst() {
  const state = useContext(RootStateContext)
  if (state === null) {
    throw new Error('')
  }
  return state
}

DataModel.ts:

export const DataModel = types
  .model('DataModel', {
    translations: types.optional(TranslationsDataModel, {} as TranslationsDataModelInstance),
  })
  .views((self) => ({
    get root() {
      return getRoot(self)
    },
  }))
  .actions((self) => ({
    fetchDataset: flow(function* fetchDataset<T>(dataUrl: string) {
      try {
        const dataset: T[] = yield fetchCsvDataset<T>(dataUrl)
        return dataset
      } catch (error) {
        console.log(`! Error: ${error}`)
        return []
      }
    }),
  }))

export interface DataModelInstance extends Instance<typeof DataModel> {}

TranslationsDataModel.ts:

const TranslationDatum = types.model('TranslationDatum', {
  key: types.string,
  text: types.string,
})

export const TranslationsDataModel = types
  .model('TranslationsDataModel', {
    translationsDataset: types.optional(t.array(TranslationDatum), []),
  })
  .views((self) => ({
    get root(): DataModelInstance {
      return getRoot(self)
    },
  }))
  .views((self) => ({
    getTranslation(key: string) {
      return '...'
    }
  }))
  .actions((self) => ({
    updateTranslationsDataset(dataset: Translation[]) {
      self.translationsDataset.replace(dataset)
    },
  }))
  .actions((self) => ({
    async afterCreate() {
      const translationsDataset = await self.root.fetchDataset<Translation>('translations.csv')
      self.updateTranslationsDataset(translationsDataset)
    },
  }))

export interface TranslationsDataModelInstance extends Instance<typeof TranslationsDataModel> {}

I used the stores in that way:

const Homepage = observer(() => {
  const {
    data: {
      translations: { getTranslation },
    },
  } = useMst()

  return (
    <div>{getTranslation('title')}</div>
  )
})

at line const translationsDataset = await self.root.fetchDataset<Translation>('translations.csv') I get Uncaught (in promise) TypeError: self.root.fetchDataset is not a function. What’s the problem?

2

Answers


  1. The issue you’re facing is related to the scope of the fetchDataset function. In your TranslationsDataModel, you’re trying to call self.root.fetchDataset to access the fetchDataset function from the DataModel. However, the fetchDataset function is defined inside the actions block of the DataModel, and by default, these actions are not accessible from other models.

    To fix this, you need to expose the fetchDataset function in a way that it can be accessed from other models. One common approach is to define it as an action in the root model (StateModel) instead of the DataModel.

    Login or Signup to reply.
  2. Here in your example fetchDataset function is defined in the DataModel, and you’re trying to call it using self.root.fetchDataset from TranslationsDataModel, however, the fetchDataset function is not directly accessible through self.root and this should be the reason why the error occured

    solution:

    you should define an action in TranslationsDataModel that calls the fetchDataset function from the DataModel:

    export const TranslationsDataModel = types
      .model('TranslationsDataModel', {
        translationsDataset: types.optional(t.array(TranslationDatum), []),
      })
      .views((self) => ({
        get root(): DataModelInstance {
          return getRoot(self)
        },
      }))
      .views((self) => ({
        getTranslation(key: string) {
          return '...'
        }
      }))
      .actions((self) => ({
        updateTranslationsDataset(dataset: Translation[]) {
          self.translationsDataset.replace(dataset)
        },
        async fetchTranslationsDataset() {
          const translationsDataset = await self.root.fetchDataset<Translation>('translations.csv')
          self.updateTranslationsDataset(translationsDataset)
        },
      }))
      .actions((self) => ({
        async afterCreate() {
          await self.fetchTranslationsDataset();
        },
      }));
    
    export interface TranslationsDataModelInstance extends Instance<typeof TranslationsDataModel> {}
    

    You can now call self.fetchTranslationsDataset() within the afterCreate action to initiate the data fetching process using the function from the parent model DataModel.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search