5

I have looked through the mobx-state-tree documents and even the test file https://github.com/mobxjs/mobx-state-tree/blob/master/packages/mobx-state-tree/tests/core/reference-custom.test.ts#L148 to figure out how to populate a reference node from an async source. For instance, user1 is loaded and references user2, but user2 is not in the tree, so go fetch user2 and load it. In the past I had all users being loaded early on, so types.late() worked just fine. But I've stopped loading all the users at the beginning of load time, and instead want only load user data that is being referenced.

Here's an example snippet. I've not actually got this example working in a sandbox, because I'm looking for instructional help from you good folks out there on the logic behind where and how to async-fetch missing nodes. You'll notice that the two users being loaded at store creation reference a third user who's not loaded yet. When MST it populating the tree with the first two users and comes across a reference to an unloaded user id, how do we make it fetch the user and then add it to the users map?

export const User = types
      .model("User", {
        id: types.identifier,
        name: types.string,
        friends: types.array(types.late(types.reference(User)))
      })
      .preProcessSnapshot(snapshot => {
        if (snapshot){
            return({
              id: snapshot.id,
              name: snapshot.name,
              friends: snapshot.friends
            })
        }
      })

    export const UserStore = types
      .model("UserStore", {
        users: types.map(User),
      })
      .actions( self => ({
        fetchUser: flow(function* (userId) {
          let returnThis
          try {
            returnThis = yield ajaxFetchUser(userId) 
            self.users.put(returnThis)
          } catch (error) {
            console.log(error)
          }
          return self.users.get(returnThis.id)
        })
      }))

    UserStore.create({
      users:[{
        id: 1,
        name: "Justin",
        friends: ["2","3"]
      },{
        id: 2,
        name: "Jack",
        friends: ["1","3"]
      }]
    })

    const ajaxFetchUser = (userId) => {
      // pretend that userId is 3
      setTimeout(()=>{
        return {
          id:3,
          name: "John",
          friends: ["1","2"]
        }
      }, 3000)
    }

Of course the scripts I'm using are much more complicated than this code, having appropriate map.put and map.get actions.

If anyone could throw some light on this subject, I'd much obliged.

  • @mweststrate, Michel Weststrate, if you have any advice I'd greatly appreciate it. – Justin Cathey Dec 19 '19 at 08:59
  • I don't think there's any out-of-the-box recipe for such scenario. Sometimes you should come up with your own ;) – jayarjo Dec 29 '19 at 09:41
  • @jayarjo do you have a hint to get me started? if preProcessSnapshot had access to the store actions I could have created a solution by now. But it seems that preProcessSnapshot and snapshotProcessor don't give us any way to access other store actions. – Justin Cathey Jan 10 '20 at 11:44
  • Did you find a solution to your question? I'm planning to do the same thing – daydr3amer Aug 26 '22 at 12:30

1 Answers1

1

You could try to intercept someone attempting to access a particular property (in your case reference) by turning it into a getter (basically make a view) and schedule an action (data fetching) from there. I know it looks like you are violating something there (and you are), but that's what you are after. I think that's how MobX operates under the hood anyway and maybe the fact that it was being violating best practices pushed its author to switch to using proxies instead. Hm... you might want to try a Proxy too.

jayarjo
  • 16,124
  • 24
  • 94
  • 138