ConrabOpto / mst-query

Query library for mobx-state-tree
MIT License
116 stars 8 forks source link

Question on usage of single record model #41

Closed geohuz closed 1 year ago

geohuz commented 1 year ago

I need to create a store which has a model contains single record: {token: "...."}, which is:

const AuthModel = types.model("AuthModel", {
  token: optional(string, "none")
})

to execute the login api(a post method), I have the following mutation:

const Login = createMutation('Login', {
  request: types.model({email: "", pass: ""}),
})

I need to use the result of the login mutation to populate the AuthModel, so I finally wrote the AuthModel as below (without creating a store):

const AuthModel = types.model("AuthModel", {
  token: optional(string, "none")
})
.props({
    login: optional(Login, {})
  })
  .actions(self=> ({
    loginUser: flow(function* (request) {
      const qry = yield* self.login.mutate({
        endpoint: api.login,
        request: request
      })
      let result = qry()
      self.token = self.login.data?.token
      return result
    })
  }))

then the rootStore:

export const RootStore = createRootStore({
  auth: optional(AuthModel, {})
})

and lastly I can use the token in the rootStore.auth.token, while it works but I'm not sure if I understand correctly or there is a better way to structure the data model as above. (A single record model)

k-ode commented 1 year ago

It's perfectly valid to use non-identifier models as data types for mutations and queries. The difference is that you don't need to use MstQueryRef or createModelStore for these, as they will not get normalized.

For things like Auth, I like to call the models services.

const AuthModel = types.model("AuthModel", {
  token: optional(string, "none")
})

const Login = createMutation('Login', {
  data: AuthModel,
  request: types.model({email: "", pass: ""}),
})

const AuthService = types.model("AuthService", {
    login: optional(Login, {})
  })
  .views(self => ({
    get token() {
      return self.login.data?.token;
     }
  }))
  .actions(self=> ({
    loginUser: flow(function* (request) {
      const qry = yield* self.login.mutate({
        endpoint: api.login,
        request: request
      })
      let result = qry()
      return result
    })
  }))

const RootStore = createRootStore({
  authService: optional(AuthService, {})
})
geohuz commented 1 year ago

@k-ode Thanks for the answer! Now I have more understanding on the mst-query, I have one more question: when I access the rootStore.authService.token for the first time it is undefined, it is possible to init the model to "none"? Or I just put some logic in the get token() view to return "none"?, in vanilla mst, we have:

const AuthModel = model("AuthModel", {
  token: "none"
})

const RootStore = model({
  authStore: optional(AuthModel, {})
}) 

const rootStore = RootStore.create()

console.log(rootStore.authStore.token) // none
k-ode commented 1 year ago

Yes, i would put "none" as a default value in a view in this case. The data property is really what you get from the server.

geohuz commented 1 year ago

@k-ode Ok, I got it.