reactjs 使用乐观钩子错误

eoxn13cs  于 11个月前  发布在  React
关注(0)|答案(3)|浏览(102)

在使用React的useOptimistic钩子时,我得到了一个奇怪的行为。
运行:
“next”:“^14.0.4”与应用程序路由器。
当我点击按钮时,我立即得到2个控制台日志输出。
在第一个日志中,我可以看到乐观的结果:

{
    "__typename": "GuideBlocksFormWebsite",
    "id": "1",
    "value": true,
    "pending": true
}

字符串
然后在它恢复到第二个日志中所示的init状态后立即执行(不是所需的状态,因为值应该更新为true):

{
    "__typename": "GuideBlocksFormWebsite",
    "id": "1",
    "value": false
}


SSR页面:

import { cookies } from 'next/headers'
import { createClient } from '@/utils/supabase/server'
import { Form } from './Form'

export const dynamic = 'force-dynamic'

const SSRPage = async () => {
  const cookieStore = cookies()
  const supabase = createClient(cookieStore)

  const { data: form } = await supabase.from('forms_progress').select()

  return (
    <>
      <Form initFormData={form} />
    </>
  )
}

export default SSRPage


initFormData看起来像这样:

form: [
  {
    id: '97b1672d-b119-473e-958c-8d1c0902fc09',
    'mrkoll.se': false,
    'eniro.se': false,
    'merinfo.se': false,
    'hitta.se': false,
    'ratsit.se': false,
    is_completed: false,
    created_at: '2023-12-05T23:26:54.592581+00:00',
    updated_at: '2023-12-05T23:26:54.592581+00:00',
    'birthday.se': false
  }
]


形式:

'use client'

import { useOptimistic, useState } from 'react'
import TickButton from './TickButton'

const keysToRender = ['mrkoll.se', 'eniro.se', 'merinfo.se', 'hitta.se', 'ratsit.se', 'birthday.se']

export const Form = ({ initFormData }) => {
  const [form, setForm] = useState(initFormData[0])
  const [optimisticTick, addOptimisticTick] = useOptimistic(form, (state, newValue) => {
    return {
      ...state,
      [newValue.key]: newValue.value,
    }
  })

  const renderButtons = () => {
    return keysToRender.map((key) => (
      <TickButton
        key={key}
        id={key}
        value={optimisticTick[key]}
        addOptimisticTick={addOptimisticTick}
      />
    ))
  }

  return <>{optimisticTick && renderButtons()}</>
}


TickButton:

'use client'

import { useTransition } from 'react'
import useGuideForm from '@/app/hooks/useGuideForm'
import { useRouter } from 'next/navigation'

const TickButton = ({ id, value, addOptimisticTick }) => {
  const { updateFormsProgress } = useGuideForm()
  const [, startTransition] = useTransition()
  const router = useRouter()

  const handleOnClick = async () => {
    startTransition(() => {
      addOptimisticTick({ key: id, value: true, pending: true })
    })

    await updateFormsProgress({
      [id]: true,
    })

    router.refresh()
  }

  return (
    <button type="button" onClick={handleOnClick} disabled={value === true}>
      {value ? 'Ready' : 'Confirm'}
    </button>
  )
}

export default TickButton


updateFormsProgress:

const updateFormsProgress = async (data: unknown) => {
    try {
      if (user) {
        const { error } = await supabase
          .from('forms_progress')
          .update(data)
          .eq('id', user?.id as string)

        if (error) {
          throw new Error(error.message)
        }
      }
    } catch (error) {
      captureException(error)
    }
  }


在这里播放的视频的行为:video
流程:

  • Button的值为false,则在SSR期间获取数据,并将其作为init值传递给useOptimistic钩子。
  • 按钮,乐观值 Flink 。
  • 按钮 Flink 后返回假值。
  • 按钮,则value被正确设置为true。

你对这里发生的事有什么线索吗?

9gm1akwq

9gm1akwq1#

我在尝试实现useOptimistic钩子时遇到了类似的问题,而没有求助于服务器操作,revalidateTag和'no-cache'。虽然我不能确定为什么你的代码没有按预期运行,但它似乎应该。我不确定它是否可能是某种缓存问题。
然而,可能的解决办法可能涉及以下步骤:
1.实现服务器操作:

'use server'

import { revalidateTag } from 'next/cache'

export const mutateFormProgress = async (keyToUpdate, rowId) => {
  await fetch(
    `http://localhost:3000/api/form-progress?rowId=${rowId}`,
    {
      method: 'POST',
      cache: 'no-cache',
      body: JSON.stringify({
        keytoUpdate,
      }),
      headers: {
        'Content-type': 'application/json',
      },
    }
  )

  revalidateTag('form-progress')
}

字符串
1.修改onClick处理程序:

const handleOnClick = async () => {
  startTransition(() => {
    addOptimisticTick({ key: id, value: true, pending: true })
  })

  await mutateFormProgress(keyToUpdate, rowId)
}


1.在SSR页面上执行fetch调用:

const response = await fetch(
  `http://localhost:3000/api/form-progress}`,
  {
    cache: 'no-cache',
    next: {
      tags: ['form-progress'],
    },
  }
)
const formData = await response.json()


此外,创建一个API端点(例如,API/form-progress)非常重要,该端点用于从数据库表中获取(GET)和变更(POST)数据。

umuewwlo

umuewwlo2#

你需要确保你更新的是传入useOptimistic钩子的原始状态,而不仅仅是调用addOptimisticTick函数。
我认为react正在等待原始状态(Tick)更新,因为这就是为什么你想调用useOptimistic,但是原始状态没有更新,所以它停止使用乐观值并恢复到旧状态。
因此,您需要为传递到组件中的prop调用更新状态函数,useOptimistic不能替代该prop值

const [optimisticTick, addOptimisticTick] = useOptimistic(
    tick, 
    (prevTick, newTick) => {
        ...prevTick,
        ...newTick,
        pending: true
    }
)

const handleOnClick = (event) => {
    startTransition(() => {
      addOptimisticTick({value: true});
      // you need to update the original tick that was passed as the prop
     
      // Generally you make some sort of API call here
      // const tickResponse = await GENERIC_API_CALL(event);
      // tickResponse will be the updated tick so = {value: true}

      // setTick((prev) => { ...prev, ...tickResponse} // pending will go back to false or undefined but value will update
    });

    handleTickPage(event)
  }

字符串

ddrv8njm

ddrv8njm3#

尝试删除你的Router OnClick函数中的router.refresh()调用。我认为这个调用可能会导致组件重新呈现,并且由于数据是在SSR期间获取的,并作为初始值传递给useOptimistic钩子,因此状态可能会重置为初始值。
试着这样做:

const handleOnClick = async () => {
  startTransition(() => {
    addOptimisticTick({ key: id, value: true, pending: true })
  })

  await updateFormsProgress({
    [id]: true,
  })

  // Fetch the new data
  const { data: newForm } = await supabase.from('forms_progress').select()
  
  // Update the state with the new data
  setForm(newForm[0])
}

字符串

相关问题