Search code examples
javascriptreactjsstoremobx

In mobX can a store "listen" to other another store, and execute a query whenever the other store changes?


Say I have a store, which has a list of organizations, the user can "select" organizations, which are then stored in the "filter" array.

export class OrganizationStore extends ArrayStore {
    //organizations = new Map();

    constructor( ...args) {
        super(args);
        this.organizations = new Map();
        this.filter = [];
        this.getter_url = '/organization/get-organizations';
    }

    async getData() {
        const full_url = new URL(this.getter_url);
        const options = {};
        options.method = 'GET';
        options.credentials = process.env.NODE_ENV === 'production' ? 'same-origin' : 'include';

        const response = await fetch(full_url, options);
        if (response.ok) {
            const d = await response.json();
            this.buildStore(d);
        }

    }

    buildStore(values) {
        this.organizations.clear();
        for (const {id, name} of values) {
            this.organizations.set(id, new Organization(id, name));
        }
    }

    get count() {
        return this.organizations.size;
    }
}

decorate(OrganizationStore, {
    organizations: observable,
    filter: observable,
    count: computed,
});



export class Organization {
    constructor(id, name) {
        this.id = id;
        this.name = name;
    }
}

Another store also exists

export class UserStore extends ArrayStore {
    constructor(organizationStore, ...args) {
        super(args);
        this.users = [];
        this.getter_url = '/users/get-users';
        this.organizationStore = organizationStore;
    }

    async getData() {
        const full_url = new URL(this.getter_url);
        const options = {};
        options.method = 'GET';
        options.credentials = process.env.NODE_ENV === 'production' ? 'same-origin' : 'include';

        query = {filter: this.organizationStore.filter()};
        //how to make this line "observe" the original store

        Object.keys(query).forEach(key => full_url.searchParams.append(key, options.query[key]));


        const response = await fetch(full_url, options);
        if (response.ok) {
            const d = await response.json();
            this.buildStore(d);
        }


    }
}

Now (how) can I make the store automaticaly refresh itself (let getData rerun once organizationStore.filter[] changes)?


Solution

  • I think you are probably looking for reactions.

    Reaction - A variation on autorun that gives more fine grained control on which observables will be tracked. It takes two functions, the first one (the data function) is tracked and returns data that is used as input for the second one, the effect function.

    export class UserStore extends ArrayStore {
        constructor(organizationStore, ...args) {
            super(args);
            this.users = [];
            this.getter_url = '/users/get-users';
            this.organizationStore = organizationStore;
    
            // Dispose of this when done with it
            const disposer = reaction(
                () => this.organizationStore.filter.length,
                () => this.getData()
            );
        }
    
        // ...
    }
    

    Another option, if you want event more control, is to use observe

    const disposer = observe(this.organizationStore.filter, (change) => {
        // Change is an object with a couple properties that describe what has changed and how
    });
    

    But I think reaction is fine for you.