mobxjs / mst-gql

Bindings for mobx-state-tree and GraphQL

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

useQuery is not reactive

alisman opened this issue · comments

**Let start by saying thank you. This library looks amazing, well documented, and I really want to use it. But I don't get this one bit.

Suppose I have a paginated list of TODO items. The page number I am currently displaying is a piece of local application state which, as I understand it, should be kept in a property of the app store (add using properties method of the base store type. If I mutate that page number, any component that references it will re-render accordingly in the normal Mobx-React way. However, as others have noted, useQuery, which fetches the set of TODO items to display will NOT re-fire (as it would with an old fashioned fetch inside useEffect). So, effectively the data is NOT reactive.

Apparently the idiomatic way to solve this problem in mst-qgl is by using setQuery in callback to pagination clicks and passing it the return of an action that loads items and returns a new query object. When I call that action, i need to pass the new page number. Great! Except that I now need to update the page number state inside an action with the name "loadItems", which is doable, but makes no sense.

When I click a page number, the conceptual action I am firing is that I am setting the page number state. Everything else is a computation of that state. I am not loading data. If data needs to load because I have changed the local state, that should be handled reactively. The component which references page number should re-render and the userQuery inside should detect that a dependency has changed and it needs to refetch data.

So,

  1. I don't get why useQuery does not behave like useEffect in re-firing when its deps change
  2. I don't understand this setQuery. It seems to require that I call these "load" actions all over the place in order to get hold of the new query.

Can anyone (@mweststrate) explain the reason it works this way?

@jamonholmgren could you comment on this?

I'm afraid I can't. I only worked a couple of weeks on this project three and half years ago, so I'm afraid I don't have any context anymore 😅.

I possibly have a same case and can provide a simplified context

In my app, i have two components ComponentA and ComponentB that use user data. They're rendered on a same page and both use same query for getting user data

It's first component that uses user data and it's not rendered if user aren't authenticated

const ComponentA = observer(() => {
   const { data, loading, store } = useQuery((store: RootStoreType) => store.queryCurrentUser({}), { fetchPolicy: 'cache-only' })
   const user = data?.currentUser
   if (!user) return null

   return `${user.firstName} ${user.lastName}`
})

It's second component. It has user data and the login button. When users click on the login button, then they get a token, the app sets a token to the httpClient of RootStore, and it refetches an user with query.refetch() from the local useQuery. This component render user data after refetching, but component above doesn't

const ComponentB = observer(() => {
   const { data, loading, store, query } = useQuery((store: RootStoreType) => store.queryCurrentUser({}))
   const user = data?.currentUser

  return (
    <>
      {user && <p>{user.firstName} {user.lastName}</p>}

      <button
        type="button"
        onClick={() => {
           const token = await getTokenSomeWay() // it's declared somewhere and give us an user token
           store.gqlHttpClient.setHeaders({ Authorization: `Bearer ${token}` })
           query?.refetch()
        }}
      >
        Login
      </button>
    </>
  )
})

I assume it's not reactive behavior of useQuery

I expected ComponentA will get updated user data too. How can i share data of the one query between components?