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
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.
Here in your example
fetchDataset
function is defined in theDataModel
, and you’re trying to call it usingself.root.fetchDataset
fromTranslationsDataModel
, however, thefetchDataset
function is not directly accessible throughself.root
and this should be the reason why the error occuredsolution:
you should define an action in
TranslationsDataModel
that calls thefetchDataset
function from theDataModel
:You can now call
self.fetchTranslationsDataset()
within theafterCreate
action to initiate the data fetching process using the function from the parent modelDataModel
.