MobX - call method from another store - javascript

I have two MobX stores:
export default class AccountStore {
accounts : Account[] = [];
constructor() {
makeAutoObservable(this);
}
loadAccounts = async () => {
//call to loadOthers
}
}
and
export default class OtherStore {
others : Others[] = [];
constructor() {
makeAutoObservable(this);
}
loadOthers = async () => {...}
}
In my AccountStore class, in my loadAccounts function I want to make a call to loadOthers from the other MobX store. How can I make this call?

Depends on how you initialize your stores. The most simple way is to have singleton stores, that way you can just import it directly.
Another way is you have some sort of root store, which initializes all other store, and pass itself to every store too, that way you have reference to the root store and to every other store from any store. Something like that:
class RootStore {
constructor() {
this.accountStore = new AccountStore(this)
this.otherStore = new OtherStore(this)
}
}
class AccountStore() {
constructor(rootStore) {
this.rootStore = rootStore.
}
loadAccounts = async () => {
this.rootStore.otherStore.callOthers()
}
}
Third way is just pass OtherStore instance to loadAccounts function. For example if you want to call loadAccounts when some React component mounts you can do that in useEffect, just get both store, and pass one to another:
export default class AccountStore {
// ...
loadAccounts = async (otherStore) => {
// ...
otherStore.loadOthers()
}
}

Related

How can we use a mobx store in utility function?

How can we use a mobx store in utility function?
I have a mobx store and a utility function to make a axio call, I want to use stote value in the utility, how can I do this?
// Store example
export default class SampleStore {
#observable title = "Coding is Love";
#observable user = {
userId: 1,
};
#action
setUser(user) {
this.user = user;
}
#action
updateUser(data) {
this.user = { ...this.user, ...data };
}
#action
clearUser() {
this.user = undefined;
}
#action
setTitle(title) {
this.title = title;
}
}
// Utility function in different file
export function makeApiCall () {
// Use SampleStore here
}
Depends on how you actually initialize your store, how your app is organized and many other factors.
Most simple way is to have singleton store and then you just import it and use directly:
// export an instance instead
export const sampleStore = new SampleStore()
// .. util fil
import { sampleStore } from './SampleStore.js'
export function makeApiCall () {
sampleStore.setUser()
}
Another way is just to pass store to the function, for example if you want to make this call inside useEffect or something:
// Make function accept store as an argument
export function makeApiCall (sampleStore) {
sampleStore.setUser()
}
// ... inside of some React component
// get store from the context (I guess you will have it at that point)
const { sampleStore } = useStores()
useEffect(() => {
// and just pass to the function
makeApiCall(sampleStore)
}, [])

Using react mobx actions from another stores inside components and other stores

in my react native app I want to use mobx for state management, my store is divided in multiple stores/files and since I want to be able to call a store actions from another stores I'm implementing a GlobalStore where I instantiate the other stores.
I want to be able to do something like this from my components
import { PostStore } from '../stores/PostStore.js'
import { UserStore } from '../stores/UserStore.js'
import { VenueStore } from '../stores/VenueStore.js'
class GlobalStore
{
postStore = new PostStore(this);
userStore = new UserStore(this);
venueStore = new VenueStore(this);
}
export default new GlobalStore;
This makes it so that using react-native Context-Provider API I can call every store action in ALL my compoennts using globalStore as a link:
In any component I can do:
globalStore.postStore.listPosts()
However I'm still not sure how I can access other store actions from within OTHER STORES.
What if inside postStore I want to use spinnerStore (to show axios calls pending, error or success status):
#action.bound getPosts = flow(function * (payload)
{
this.spinnerStore.setData({status: 1});
try
{
this.spinnerStore.setData({status: 2, response: response});
let response = yield axios.get('/api/posts', { params: payload })
return response;
}
catch (error) {
console.error(error);
this.spinnerStore.setData({ status: 3, errors: error });
throw error;
}
})
Here spinnerStore would be undefined...
However I'm still not sure how I can access other store actions from within OTHER STORES.
When you instantiate a store you can assign one of its properties to be another store instance. This is what you included in your example
class Foo {
constructor(instance) {
this.instance = instance
}
}
class Bar {}
const foo = new Foo(new Bar());
foo.instance instanceof Bar; // true
Now your foo instance has access to any public properties/methods defined on Bar
class Foo {
constructor(instance) {
this.instance = instance
}
}
class Bar {
#observable isLoading = false;
#observable data = null;
#action
getData() {
this.isLoading = true;
return axios.get('/foo').then((data) => {
this.isLoading = false;
this.data = data;
}).catch(e => {
this.isLoading = false;
});
}
}
const foo = new Foo(new Bar());
// in react
const MyComponent = ({ foo }) => (
<div>
{foo.instance.isLoading && <Spinner />}
<button onClick={foo.instance.getData}>
will call Bar action from Foo store
</button>
</div>
);
export default lodash.flowRight(
mobx.inject((stores) => ({ foo: stores.fooStore })),
mobx.observer
)(MyComponent)
In your example with generators, you cannot use fat arrows so this isn't bound to your class instance anymore, which is why it will be undefined. Using promises and fat arrows solves that problem.

React Native: Calling a function that's inside a functional component from a class

I'm lost on how functional components and class components interact. How do I call a functional component's function from inside a class?
I'm trying to call Initialize()
App.js:
import Firebase from './components/Firebase';
export default class App extends React.Component {
componentDidMount() {
// Call Initialize()
}
Firebase.js:
const Firebase = () => {
const Initialize = () => {
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
console.log("Firebase is initialized");
}
}
export default Firebase;
I think the confusion is more related to JavaScript than any JavaScript library/framework.
You are trying to call a private function, only available in the Firebase closure function.
If you want to set available the Initialize function, like a "static" method, declare it as property:
const Firebase = () => {
...
};
Firebase.Initialize = () => {
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
console.log("Firebase is initialized");
};
export default Firebase;
// and then
export default class App extends React.Component {
componentDidMount() {
Firebase.Initialize()
}
}

How to call a function to change the state within a callback function?

I am implementing a Welcome Display web app that takes a guest name received from RabbitMQ and populates it on the screen. In the callback function of the stompClient.subscribe(... I want to call the function to change the state of the reservation and view on the screen. When I call the function it says the function is not defined. How can I change the state every time I receive the message?
import React from 'react';
import '../css/App.css'
import WelcomeVisitor from '../pages/WelcomeVisitor';
import ThankYou from '../pages/ThankYou';
import Stomp from 'stompjs'
class App extends React.Component {
constructor(props){
super(props)
this.state = {
currentView: 'ThankYou',
currentReservation: null
}
this.siteId = props.match.params.siteId
this.bayNumber = props.match.params.bayNumber
this.changeView = this.changeView.bind(this)
this.connectRabbit = this.connectRabbit.bind(this)
}
changeView(view){
this.setState({
currentView: view
})
}
changeReservation(reservation){
this.setState({
currentReservation: reservation
})
}
render(){
let view = ''
this.connectRabbit(this.siteId, this.bayNumber)
if(this.state.currentView === 'ThankYou'){
view = <ThankYou changeView={this.changeView}/>
} else if(this.state.currentView === 'WelcomeVisitor') {
view = <WelcomeVisitor guestName='Quinton Thompson'/>
}
return (
<div className="App">
{view}
</div>
)
}
connectRabbit(siteId, bayNumber){
let stompClient
var ws = new WebSocket('ws://localhost:15674/ws')
const connectHeaders = {
'login': 'guest',
'passcode': 'guest',
}
const queueHeaders = {
'x-queue-name': `${bayNumber}.visit.out.display`,
'durable': 'true',
'auto-delete': 'false'
}
stompClient = Stomp.over(ws)
stompClient.connect(connectHeaders , function(frame){
console.log('Connected')
stompClient.subscribe('/exchange/ds.game/visit.out',function(message){
//changeReservation and changeView is not defined
this.changeReservation(message.body)
this.changeView('WelcomeVisitor')
}, queueHeaders)
console.log('here')
})
}
}
export default App;
The this object in your function callback is likely not referencing the this object in your class.
Changing the function syntax to: (message) => {} and (frame) => {} should make it work. See below:
stompClient.connect(connectHeaders ,(frame) => {
console.log('Connected')
stompClient.subscribe('/exchange/ds.game/visit.out', (message) => {
//changeReservation and changeView is not defined
this.changeReservation(message.body)
this.changeView('WelcomeVisitor')
}, queueHeaders)
console.log('here')
})
While the code snippet above would make your code work,
ideally we should avoid writing these types of callback initializations on the fly ( in render method ), maybe better way of doing it would be creating function calls and referencing those as callbacks. Something like this ( more improvements can be made but just as an example ) :
connectCallback(stompClient, queueHeaders, frame) {
console.log('Connected');
stompClient.subscribe('/exchange/ds.game/visit.out', (message) => {
this.subscribeCallback(message)
}, queueHeaders);
}
subscribeCallback(message) {
this.changeReservation(message.body)
this.changeView('WelcomeVisitor')
}
Then just use the two functions above as a callback in your render code.
Lastly, you might need to bind changeReservation(reservation) also before anything else.

How to bind the function by use #inject?

When I use the mobx-react ,I use inject decorator to transmit the store.But when I get the store such as
#inject("store") #observer
class item extends Component {
constructor() {
this.store = this.props.store;
}
}
But when I want to call the function of store such as store.getUser() , I found that the context getUser function is not this , how can I bind this to the store ?
PS: the store is such as following :
class Store {
#observable user = "Sarah";
#computed
get getUser() {
return user + "Ok";
}
}
export default new Store();
I use the getUser like
render() {
<div>{this.store.getUser()}</div>
}
class Store {
#observable user = "Sarah";
#computed
get okUser() {
return this.user + "Ok";
}
}
const store = new Store();
console.log(store.okUser);
#computed is getter so you do not need to call it as function.
You have to use this.user in your store:
class Store {
#observable user = "Sarah";
#computed
get getUser() {
return this.user + "Ok";
}
}
export default new Store();
A computed is a getter, so you don't access it with a function call. Just dereference the field:
render() {
<div>{this.store.getUser}</div>
}

Categories