UseMutation не запускает повторный рендеринг React

Пройдя курс Fullstackopen, я прохожу часть 6 и изучаю useQUery/useMutation.

Я застрял ближе к концу, потому что мое приложение не будет повторно отображаться после того, как я вызову API и внесу изменения в список элементов. Я попробовал fetchPolicy:'false' в параметрах запроса.

Другая логика работает: мне нужно добавить элемент и обновить его, но на вкладке сети инструментов разработки я вижу, что при запуске обновления я получил запрос «PUT», но за ним не последовал запрос «GET», так что я думаю рендеринг, который должен быть запущен с помощью «onSuccess», не запускается.

Я не понимаю, почему, и я покопался в Интернете и здесь, чтобы найти какой-то вариант использования, но не смог понять.

Спасибо всем!

Вот App.js:

import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query'
import { getAnecdotes, updateAnecdote } from './request'

import AnecdoteForm from './components/AnecdoteForm'
import Notification from './components/Notification'

const App = () => {

  const queryClient = useQueryClient()

  const updateAnecdoteMutation = useMutation({
    mutationFn: updateAnecdote,
    onSuccess: (updatedAnecdote) => {
      queryClient.setQueryData(['anecdotes', updatedAnecdote.id], updatedAnecdote)
    }
  })

  const handleVote = (anecdote) => {
    updateAnecdoteMutation.mutate({ ...anecdote, votes: anecdote.votes + 1 })
  }

  const result = useQuery({
    queryKey: ['anecdotes'],
    queryFn: getAnecdotes,
    refetchOnWindowFocus: false
  })

  if (result.isLoading) {
    return <div>loading data...</div>
  }
  if (result.isError) {
    return <div>Error: {result.error.message}</div>
  }

  const anecdotes = result.data

return (...)

АнекдотФорм.js:

import { createAnecdote } from "../request"


const AnecdoteForm = () => {

  const queryClient = useQueryClient()

  const newAnecdoteMutation = useMutation({
    mutationFn: createAnecdote,
    onSuccess: (newAnecdote) => {
      const anecdotes = queryClient.getQueryData(['anecdotes'])
      queryClient.setQueryData(['anecdotes'], anecdotes.concat(newAnecdote))
    }
  })

  const onCreate = (event) => {
    event.preventDefault()
    const content = event.target.anecdote.value
    event.target.anecdote.value = ''
    console.info('new anecdote')
    newAnecdoteMutation.mutate({ content, votes: 0 })
  }
return (...)

запрос.js:


const baseUrl = 'http://localhost:3001/anecdotes'

export const getAnecdotes = () =>
    axios.get(baseUrl).then(res => res.data)

export const createAnecdote = (newAnecdote) => {
    axios.post(baseUrl, newAnecdote).then(res => res.data)
}

export const updateAnecdote = (updatedAnecdote) => {
    axios.put(`${baseUrl}/${updatedAnecdote.id}`, updatedAnecdote).then(res => res.data)
}
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
0
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Анализ проблемы:

1. Обновление кэша:

React-query — это то, что вы используете для кэширования и выборки данных. Вы используете queryClient.setQueryData для обновления кэшированных данных для этого конкретного анекдота после его изменения с помощью updateAnecdoteMutation. Возможно, ваш список не будет перерисовываться повторно, поскольку вы обновляете кеш только для конкретного анекдота, а не для всей коллекции анекдотов.

2. Отсутствие автоматического обновления:

Вы сообщили, что на вкладке сети отображается запрос «PUT», но после него не отображается запрос «GET». Это говорит о том, что после мутации не происходит автоматического обновления, скорее всего, потому, что queryClient.setQueryData не инициирует повторную выборку; вместо этого он просто обновляет кэш локально.

Резолюции:

Первое решение: обновите запрос и сделайте его недействительным.

Чтобы повторно получить весь список анекдотов, используйте queryClient.invalidateQueries. Это гарантирует, что после обновления компонент повторно отрисовывается с обновленными данными и что с сервера будут получены самые последние данные.

Чтобы обновить анекдот, обновите метод onSuccess в хуке useMutation:

const updateAnecdoteMutation = useMutation({
  mutationFn: updateAnecdote,
  onSuccess: () => {
    queryClient.invalidateQueries(['anecdotes'])
  }
})

Сделав это, вы можете убедиться, что данные актуальны, сделав запрос анекдотов недействительным и заставив его перезагрузить.

Второе решение: обновить кэш списка вручную.

Убедитесь, что вы обновляете весь список в кеше, а не только конкретный анекдот, если вы хотите вручную обновить кеш списка, а не выполнять повторную выборку. Хотя этот метод предотвращает дальнейшие сетевые запросы, он требует обновления кэша вручную.

Чтобы изменить полный список анекдотов, обновите метод onSuccess:

const updateAnecdoteMutation = useMutation({
  mutationFn: updateAnecdote,
  onSuccess: (updatedAnecdote) => {
    queryClient.setQueryData(['anecdotes'], (oldData) => {
      return oldData.map(anecdote =>
        anecdote.id === updatedAnecdote.id ? updatedAnecdote : anecdote
      )
    })
  }
})

Интерпретация:

queryClient.invalidateQueries: эта функция делает запросы, использующие указанный ключ запроса, недействительными. После этого всякий раз, когда используется ключ запроса или компонент снова монтируется, ответный запрос снова автоматически извлекает данные. Это полезно, если вы хотите быть уверенным, что ваши данные постоянно актуальны, без необходимости вручную управлять изменениями кэша.

queryClient.setQueryData: этот метод используется для немедленного обновления кеша. Вы можете убедиться, что кеш обновлен с вашими данными, включив функцию обновления полного списка. Это может привести к перезагрузке вашего компонента.

Дополнительные советы:

Используйте оптимистические обновления. Если вы хотите обеспечить более плавное взаимодействие с пользователем, немедленно обновляя пользовательский интерфейс до того, как сервер ответит, вы можете использовать оптимистические обновления. Это включает в себя оптимистическое обновление локального состояния или кэша при условии, что запрос сервера будет успешным. Вот пример:

const updateAnecdoteMutation = useMutation({
  mutationFn: updateAnecdote,
  onMutate: async (updatedAnecdote) => {
    await queryClient.cancelQueries(['anecdotes'])
    const previousAnecdotes = queryClient.getQueryData(['anecdotes'])
    
    queryClient.setQueryData(['anecdotes'], (oldData) =>
      oldData.map(anecdote =>
        anecdote.id === updatedAnecdote.id ? updatedAnecdote : anecdote
      )
    )
    return { previousAnecdotes }
  },
  onError: (err, updatedAnecdote, context) => {
    queryClient.setQueryData(['anecdotes'], context.previousAnecdotes)
  },
  onSettled: () => {
    queryClient.invalidateQueries(['anecdotes'])
  }
})

В этой конфигурации:

  • Оптимистичные обновления можно выполнить, вызвав onMutate, который предшествует функции мутации.
  • В случае сбоя мутации onError позволяет вернуться к начальное состояние.
  • Независимо от результата мутации, onSettled гарантирует, что запрос признан недействительным.

Проблема повторного рендеринга после операции обновления должна быть решена, если вы позаботитесь о логике обновления кэша в вашей функции onSuccess.

Ого, это очень подробный ответ! Большое спасибо ! К сожалению, это не работает (решения 1 и 2). Должен ли я упомянуть, что перед запросом PUT был сделан первый запрос под названием OPTION? Я обновил updateAnecdoteMutation вот так: const updateAnecdoteMutation = useMutation({ mutationFn: updateAnecdote, onSuccess: () => { console.info('updating') queryClient.invalidateQueries(['anecdotes']) // queryClient.setQueryData(['anecdotes', updatedAnecdote.id], updatedAnecdote) console.info('after invalidateQueries'); } })

BattlePoap 05.07.2024 02:55
Ответ принят как подходящий

в вашем текущем коде вы получаете старый список данных и добавляете в список новый анекдот, чтобы не запускать новый вызов get для получения последнего состояния сервера.

Решение:

просто сделайте недействительным ключ anecdote следующим образом:

  const mutation = useMutation(createAnecdote, {
    onSuccess: () => {
      queryClient.invalidateQueries('anecdotes'); // invalidate and call getAnecdotes
    },
  });

вы можете применить то же самое для обновления элемента.

Вызовы API после голосования за анекдот:

Сделал это, но с массивом ['anecdotes'] или аргументом {queryKey: ['anecdotes']} То же поведение и без выборки. После этого я даже попытался добавить queryClient.fetchQuery([...]), но безуспешно.

BattlePoap 06.07.2024 00:03

Можете ли вы разместить свой проект на GitHub или codeandbox?

Yasin 06.07.2024 10:58

Обязательно: github.com/realpoap/fullstack-open/tree/main/Part%206/…

BattlePoap 06.07.2024 14:12

клонировал ваш репозиторий, и он работает так, как ожидалось. после голосования он вызывает метод get, а также обновляется пользовательский интерфейс. Я отредактировал свой ответ, чтобы показать вам фотографию вызовов API.

Yasin 06.07.2024 18:57

Это так странно! Думаю, тогда мне пора двигаться дальше. Спасибо за ваше время !

BattlePoap 06.07.2024 19:12

Другие вопросы по теме