Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

atomsWithQueryAsync infinite loop #53

Open
purecopy opened this issue Nov 21, 2023 · 11 comments
Open

atomsWithQueryAsync infinite loop #53

purecopy opened this issue Nov 21, 2023 · 11 comments

Comments

@purecopy
Copy link

purecopy commented Nov 21, 2023

I discovered that there is an infinite loop, when the queryFn from atomsWithQueryAsync throws an error and I am subscribed to the status-atom.

I created a simple reproduction: https://codesandbox.io/p/sandbox/jotai-effect-infinite-loop-simple-forked-xgt3ck

// atomsWithQuery seems to be fine
const [queryAtom, queryStatusAtom] = atomsWithQueryAsync(async () => ({
  queryKey: ["queryKey"],
  queryFn: async () => {
    throw Error("Uh-oh!") 
  },
  retry: false,
}));

function MyComponent() {
  useAtom(queryStatusAtom) // infinite loop
}
@dai-shi
Copy link
Member

dai-shi commented Nov 21, 2023

Can @leweyse take a look?

@purecopy
Copy link
Author

For me it seems like the problem lies in the baseStatusAtom. I figured notifyResult() from the callback get's called over and over again.

const baseStatusAtom = atom(async (get) => {
const observer = await get(observerAtom)
const observable = {
subscribe: (arg: { next: (result: Result) => void }) => {
const callback = (result: Result) => {
const notifyResult = () => arg.next(result)
if ((observer as any)[IN_RENDER]) {
Promise.resolve().then(notifyResult)
} else {
notifyResult()
}
}
const unsubscribe = observer.subscribe(callback)
callback(observer.getCurrentResult())
return { unsubscribe }
},
}
const resultAtom = atomWithObservable(() => observable, {
initialValue: observer.getCurrentResult(),
})
if (process.env.NODE_ENV !== 'production') {
resultAtom.debugPrivate = true
}
return resultAtom
})

@wolfgang-cognify
Copy link

We just ran into the same problem here. A fix is highly appreciated, since this currently blocks us from going to production with our app.
Thank you!

@DaniiiA
Copy link

DaniiiA commented Nov 22, 2023

I also faced this issue a while ago! Thankfully someone brings up the topic now.
Hopefully we can get a fix soon. That would be awesome 🙌

@Kunama
Copy link

Kunama commented Nov 27, 2023

I believe this issue is present in atomsWithInfiniteQuery as well. Seems to be linked to the change in this commit as downgrading to v0.7.1 causes the issue to resolve

@dai-shi
Copy link
Member

dai-shi commented Nov 27, 2023

@Aslemammad Can you help?

@ShuviSchwarze
Copy link

I thought I was going insane with this. In my use-case I was using atomsWithQueryAsync as a base atom then have another atom derives its state from the base atom for rendering. Couldn't figure out why it was looping, my initial thoughts was due to the fact that every get(baseAtom) call is triggering a rerun of the initial query and causing whatever is calling get(baseAtom) to rerun and loops.
The problem with this atom looping occurs simply with an await inside queryFn and isn't limited to just throwing the Error.
https://codesandbox.io/p/sandbox/jotai-effect-infinite-loop-simple-forked-kd3s84

@sairion
Copy link

sairion commented Dec 11, 2023

Seems the most of APIs are reimplemented in e6f6cb7 and the status atom seems to be gone, so this is not a problem from 0.8.0?

@kalijonn
Copy link
Collaborator

I've taken a different approach in v0.8.0. atomsWithQueryAsync doesn't exist anymore.

This is how I see that pattern:

const idAtom = atom<Promise<string>>(async (get) => {
  const response = await fetch('/id/from/some/where')
  return response.json()
})

const userAtom = atomWithQuery((get) => {
  const id = get(unwrap(idAtom)) // id is of type string | undefined
  return {
    queryKey: ['users', id],
    queryFn: async ({ queryKey: [, id] }) => {
      const res = await fetch(
        `https://jsonplaceholder.typicode.com/users/${id}`
      )
      return res.json()
    },
    enabled: !!id, // queryFn will only fire when id is defined
  }
})

This is how dependent queries work in tanstack/query. Ref: Dependent Queries

@ryanhaticus
Copy link

I have this issue as well. :(

@ShuviSchwarze
Copy link

I have this issue as well. :(

This issue was resolved for me with 0.8.0, atomWithQueryAsync is removed now. Should probably update the version and migrate to the unwrap approach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants