Search code examples
reactjsmobxmobx-state-tree

Structure: How to represent a search input, search query, and search results using mobx-state-tree?


I've got an app using mobx-state-tree that currently has a few simple stores:

  • Article represents an article, either sourced through a 3rd party API or written in-house
  • ArticleStore holds references to articles: { articles: {}, isLoading: bool }

Simple scenario

This setup works well for simple use-cases, such as fetching articles based on ID. E.g.

  1. User navigates to /article/{articleUri}
  2. articleStoreInstance.fetch([articleUri]) returns the article in question
  3. The ID is picked up in render function, and is rendered using articleStoreInstance.articles.get(articleUri)

Complex scenario

For a more complex scenario, if I wanted to fetch a set of articles based on a complex query, e.g. { offset: 100, limit: 100, freeTextQuery: 'Trump' }, should I then:

  1. Have a global SearchResult store that simply links to the articles that the user has searched for
  2. Instantiate a one-time SearchResult store that I pass around for as long as I need it?
  3. Keep queries and general UI state out of stores altogether?

I should add that I'd like to keep articles in the stores between page-loads to avoid re-fetching the same content over and over.

Is there a somewhat standardized way of addressing this problem? Any examples to look at?


Solution

  • What you need might be a Search store which keeps track of following information:

    • Query params (offset, limit, etc.)
    • Query results (results of the last search)
    • (Optional) Query state (isLoading)

    Then to avoid storing articles in 2 places, the query results should not use Article model, but reference to Article model. Anytime you query, the actual result will be saved in existing store ArticleStore, and Search only holds references:

    import { types, getParent, flow } from 'mobx-state-tree'
    
    const Search = types.model({
        params: // your own params info
        results: types.array(types.reference(Article))
      }).views(self => ({
        get parent() {
          return getParent(self) // get root node to visit ArticleStore
        }
      })).actions(self => ({
        search: flow(function*(params) {
          this.params = params // save query params
          const result = yield searchByQuery(query) // your query here
          this.parent.articleStore.saveArticles(result) // save result to ArticleStore
          this.results = getArticleIds(result) // extract ids here for references
        })
      }))
    

    Hope it's what you are looking for.