Search code examples
reactjsstate-managementrecoiljs

Refresh recoil atom with async fetch, after it has been updated locally?


I have an application where I show a set of invites, each invite has a set of answers connected, that the users can update,

I have the following recoil state:

export const answerState = atom<AnswerDto[] | undefined>({
  key: 'Answers',
  default: selector({
    key: 'Answers/Default',
    get: async ({ get }) => {
      const id = get(currentInviteIdState)
      if (!id) {
        return undefined
      }
      const response = await getAnswerInviteId(id)
      return response.data
    },
  }),
})

Updating the states is done with a react useCallback(), like:

export function useUpdateAnswer() {
  const [currentState, setState] = useRecoilState(answerState)

  return useCallback(
    (answer: AnswerDto) => {
      postAnswerUpdate(answer).then(({ data }) =>
        setState([
          ...(currentState?.filter((item) => item.id !== data.id) || []),
          data,
        ]),
      )
    },
    [currentState, setState],
  )
}

this part does work fine, I can update the AnswerState, and get the result shown in my app. But if I then update the initial currentInviteIdState, after I have updated a couple of answers, recoil doesn't fetch a new set of answers for this new invite.

It seems that this is as designed, according to https://recoiljs.org/docs/guides/asynchronous-data-queries#query-default-atom-values, so what am I doing wrong? I've been browsing through the docs for examples, but so far without any luck.


Solution

  • Should have read the documentation better.. Using atomFamily and selectorFamily let me do what I want, as I can specify an id.

    const answerState = atomFamily<AnswerDto[] | undefined, number>({
      key: 'Answers',
      default: selectorFamily({
        key: 'Answers/Default',
        get: (id) => ({ get }) => {
          if (!id) {
            return undefined
          }
          return getAnswerInviteId(id).then(({data}) => data)
        },
      }),
    })
    
    export const answersByOtherState = selector({
      key: 'answersbyId',
      get: ({get}) => {
        const id = get(currentInviteId)
        return get(AnswerState(id))
      }
    })