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

[atomsWithMutation] Feature Consideration: automatic dependency mutation #47

Open
sowhatdoido opened this issue Sep 28, 2023 · 4 comments

Comments

@sowhatdoido
Copy link

Would it be possible to provide an option for atomsWithMutation that will automatically trigger a mutation if the atoms within the dependency array changes? Consider your example:

import { atom, useAtom } from 'jotai'
import { atomsWithMutation } from 'jotai-tanstack-query'

const idAtom = atom(1)
const [, postAtom] = atomsWithMutation((get) => ({
  mutationKey: ['posts'],
  mutationFn: async ({ title, body }) => {
    const res = await fetch(`https://jsonplaceholder.typicode.com/posts`, {
      method: 'POST',
      body: JSON.stringify({ title, body, userId: get(idAtom) }),
      headers: { 'Content-type': 'application/json; charset=UTF-8' },
    })
    const data = await res.json()
    return data
  },
}))

const PostData = () => {
  const [post, mutate] = useAtom(postAtom)
  return (
    <div>
      <button onClick={() => mutate([{ title: 'foo', body: 'bar' }])}>
        Click me
      </button>
      <div>{JSON.stringify(post)}</div>
    </div>
  )
}

Here clicking the button will mutate two values (title: 'foo', body: 'bar' ) triggering an api call. I understand that you can modify the idAtom then call mutate to get an api result with a modified userId.

But consider a scenario where the data you're sending all consists of atoms (in this case imagine just userId, not title and body). In order to get the api call to update, you'd have to set idAtom then call an empty mutate function.

Would it make more sense to bind the mutation atom to the idAtom via something like mutationDependencies: [idAtom], such that when idAtom changes a mutation will automatically occur, skipping necessitating the mutate call all together?

@sowhatdoido
Copy link
Author

Btw, to avoid an XY problem, I'm also open to some suggestions on how you would handle a scenario like this if you wanted to avoid calling

const set = useSetAtom(idAtom);
set(2);
mutate();

every time you update something.

@dai-shi
Copy link
Member

dai-shi commented Sep 28, 2023

Sounds reasonable, but I need some comments from someone who is familiar with this issue.

We kind of know the current implementation isn't very ideal, and look for the overhaul: #42
Maybe this is also taken care of by the overhaul.
@kalijonn is working on something: #45

@kalijonn
Copy link
Collaborator

I think calling mutate automatically is a footgun considering a lot of the times state is updated as the user types or fills a form. This will cause multiple api requests with partial payload.

But I love the second idea of payload being in an atom.

Note: I updated the code sample to use the newer syntax.

import { atom, useAtom } from 'jotai'
import { atomWithMutation } from 'jotai-tanstack-query'

const idAtom = atom(1)
const payloadAtom = atom({
title: "",
body: "",
})
const postAtom = atomsWithMutation((get) => ({
  mutationKey: ['posts'],
  mutationFn: async () => {
const payload = get(payloadAtom)
const id = get(idAtom)
    const res = await fetch(`https://jsonplaceholder.typicode.com/posts`, {
      method: 'POST',
      body: JSON.stringify({...get(payloadAtom), id: get(idAtom)}),
      headers: { 'Content-type': 'application/json; charset=UTF-8' },
    })
    const data = await res.json()
    return data
  },
}))

const PostForm = () => {
  const [{title, body}, setPayload] = useAtom(payloadAtom)
  
  return //some form that uses setPayload to update info
}

const PostData = () => {
  const [{data, mutate}] = useAtom(postAtom)
  return (
    <div>
      <button onClick={() => mutate()}>
        Click me
      </button>
      <div>{JSON.stringify(post)}</div>
    </div>
  )
}

theoretically this should be possible now. theoretically this should bring some performance benefits in terms of rendering only the PostForm when input changes.

However, I'm concerned about stale closures. I'll investigate further to see if that's relevant.

@kalijonn
Copy link
Collaborator

https://codesandbox.io/p/devbox/atomwithmutation-dqktj4

this looks like a reasonable solution? adding the atom value as a mutationKey should ensure there's no stale closures.

Let me know what you think.

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

3 participants