Mobx-state-tree: [Question] How to create functional getters ( while passing arguements ), like the ids of the model you need

Created on 18 Oct 2017  路  5Comments  路  Source: mobxjs/mobx-state-tree

I have a UserStore with a map of users:

const UsersStore = t.model({
    users: t.map(User),
    loading: t.boolean

}).actions((store) => ({


    // Asynchronus actions in MST docs: https://github.com/mobxjs/mobx-state-tree/blob/master/docs/async-actions.md#asynchronous-actions-and-middleware
    loadUsers() {
        store.loading = true;
        getUsers().then(store.addUsersToMap)
    },

    addUsersToMap(usersMap) {
        store.users = usersMap;
        store.loading = false;

    }

}))

And a top level AppStore using "requestUserMap" that is mapping over the UserStore and another domain store nested inside of it:

        get requestUserMap() {

            // returns an object with request_ids as keys and the corresponding user objects
            // for each request based on the requests's shared_user_ids

            let {requestsStore, usersStore} = store;
            let requestUsers = {};
            requestsStore.requests.values().forEach(request => {
                requestUsers[request.request_id] = request.shared_user_ids.map((id) => {
                    let user = usersStore.users.get(id);
                    return user ? user : {name: "Unknown User"}
                })
            })
            return requestUsers;
        }

This works, but...

My question is, is there any way to break out getting users from the usersStore with the getter into its on function, but still have the changes to the usersStore tracked by MST and the view recompute, with something like * return usersStore.getUserById(id) * below:

            requestsStore.requests.values().forEach(request => {
                requestUsers[request.request_id] = request.shared_user_ids.map((id) => {
                    return usersStore.getUserById(id)
                })
            })

Getting users by id is not technically an action, it is simply a getter function but I'm not sure how to implement it

Most helpful comment

You can do the same with mobx-state-tree! Just return a object with a function instead of a getter in the views block :)

All 5 comments

Maybe I did'nt understood the question, but inside the .views() block of models you can define view functions, that allows just to read your state :)

@mattiamanzati Thank you for the quick response!

The above code snippets are all views, and my "requestUsersMap" does work correctly and is reactive.

I am just looking for a way to simplify the view logic a little bit, if there is a way to turn this:

// .views({
get requestUserMap() {       
           requestsStore.requests.values().forEach(request => {
                requestUsers[request.request_id] = request.shared_user_ids.map((id) => {
                    let user = usersStore.users.get(id);
                    return user ? user : {name: "Unknown User"}
                })
            })
...

into this:

get requestUserMap() {   
            requestsStore.requests.values().forEach(request => {
                requestUsers[request.request_id] = request.shared_user_ids.map((id) => {
                    return usersStore.getUserById(id)
                   // ^ This line, .getUserById is the simplification
                })
            })

2 blockers:

  1. Adding a getter function that accepts arguments, but is not an action is impossible. For example userStore.getUserById(id) _instead of referencing my userStore.users map directly every time i need userStore.users_, afaik, the only functions you can add to a model are views and actions.

  2. Even if you could add getter functions to a model, in theory they wouldn't be reactive to changes in the store because it's not a getter, it is itself a function call

I don't know how you are supposed to do this I was just wondering if there was a best practice or a workaround for getter functions.

@mattiamanzati Previously in vanilla Mobx - we were able to add simply add getter functions to a store like so:

class UserStore {

    @observable users = [];

    findUserById = (id) => {
        let user = this.users.find((user) => Number(user.user_id) === Number(id));
        return user ? user : { name: "Unknown User"}
    };
}

You can do the same with mobx-state-tree! Just return a object with a function instead of a getter in the views block :)

Answered. Closing

Was this page helpful?
0 / 5 - 0 ratings