在NextJS中使用React -query提交表单数据后,React更新视图



我正在做一个NextJS项目,我遇到了一个问题,我的视图在表单提交后没有更新。我使用react-query来获取数据并进行突变。

我有一个名为"Add Day"的表单,它通过端点向数据库添加一个日期对象。但是在进行此更新之后,我的UI没有刷新以反映这个新对象。

下面是一个演示视频:

https://i.stack.imgur.com/uHfVG.jpg

可以看到,当我刷新页面时,视图也会更新。但是我希望它能在我提交表格后立即更新,并且它是成功的。我的代码是这样设置的:

TripDetail

const TripDetailPage = () => {
const router = useRouter()
const queryClient = useQueryClient()
const tripId = router.query.tripId as string
const { data, isError, isLoading } = useQuery({
queryKey: ['trips', tripId],
queryFn: () => getTrip(tripId),
})
const [tabState, setTabState] = useState(1)
const toggleTab = (index: number) => {
setTabState(index)
}
const [modalVisible, setModalVisible] = useState<boolean>(false)
const toggleModalVisible = () => {
setModalVisible(!modalVisible)
}
const onDayAdded = () => {
queryClient.invalidateQueries({ queryKey: ['trips', tripId] })
console.log('refetching')
console.log('day was added')
}
console.log('new data?')
console.log(data)
if (isLoading) {
return <span>Loading...</span>
}
if (isError) {
return <span>Error</span>
}
if (!data || !data.data || !data.data.name || !data.data.days) {
return <span>Error</span>
}
return (
<div className="ml-12 mr-12 mt-6">
<div className="flex flex-row justify-between">
<div className="order-first flex items-center">
<h1 className="text-2xl font-semibold text-slate-800">{data.data.name}</h1>
<Button className="btn btn-primary ml-8">Invite</Button>
</div>
<div className="order-last flex">
<Button className="btn btn-primary mr-4">Refresh</Button>
<Button className="btn btn-primary" onClick={toggleModalVisible}>
Add Day
</Button>
</div>
<AddDayModal
onSuccess={onDayAdded}
modalVisible={modalVisible}
toggleModalVisible={toggleModalVisible}
trip={data.data}
/>
</div>
<div id="tabs-nav" className="font-medium text-lg border-separate pb-2 border-b-2 border-gray-200 mt-6">
<TabButton tabId={1} currentTabState={tabState} name="Plans" toggleTab={() => toggleTab(1)}></TabButton>
<TabButton
tabId={2}
currentTabState={tabState}
name="Calendar"
toggleTab={() => toggleTab(2)}
></TabButton>
<TabButton tabId={3} currentTabState={tabState} name="Ideas" toggleTab={() => toggleTab(3)}></TabButton>
</div>
<div id="content">
{tabState === 1 && <PlannerBoard days={data.data.days} tripId={tripId}></PlannerBoard>}
{tabState === 2 && <h1>Tab 2</h1>}
{tabState === 3 && <h1>Tab 3</h1>}
</div>
</div>
)
}
export default TripDetailPage

我在onDayAdded中调用invalidateQueries,它应该重新获取我的数据并更新我的视图,但它似乎不是。PlannerBoard是我传递数据的地方,是呈现列的组件。它基本上遍历传递给它的days并渲染它们。

规划师:

type TDndPlannerProps = {
days: TDay[]
tripId: string
}
const PlannerBoard = (props: TDndPlannerProps) => {
// ...
props.days.forEach((day) => {
// lots of processing, creates a data object which has the objects to make this view
})
const [state, setState] = useState<IData>(data) // after above processing
// removed a bunch of code

return (
<div className="overflow-auto">
<div>
{day.map((day) => {
return <DayColumn key={day.id} day={day} />
})}
</div>
</div>
)
}
export default PlannerBoard

AddDay模态:

type TModalProps = {
trip: TTrip
modalVisible: boolean
toggleModalVisible: () => void
onSuccess: () => void
}
const formArr: TFormField[] = [
{
label: 'Day name',
name: 'name',
type: 'text',
},
{
label: 'Day date',
name: 'date',
type: 'date',
},
]
const AddDayModal = (props: TModalProps) => {
const [day, setDay] = useState<TDay>({
name: '',
tripId: props.trip.id!,
})
const onSuccess = () => {
props.toggleModalVisible()
props.onSuccess()
}
const { mutate, isLoading, isError, error } = useMutation((day: TDay) => postDay(day), { onSuccess })
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
mutate(day)
}
const onUpdate = (update: TFormFieldValue) => {
setDay((prev) => {
if (update.type == 'date') {
return { ...prev, [update.name]: new Date(update.value) }
} else {
return { ...prev, [update.name]: update.value }
}
})
}
const formModalProps: TFormModalProps = {
title: 'Add Day',
formArr,
submitBtn: 'Submit',
submittingBtn: 'Adding',
toggleVisible: props.toggleModalVisible,
visible: props.modalVisible,
onSubmit,
isLoading,
isError,
error,
onUpdate,
}
return (
<div>
<FormModal {...formModalProps} />
</div>
)
}
export default AddDayModal

mutate在这里使axios调用我的API更新数据库与此表单数据。一旦我们到达onSuccess, API调用就成功了,它调用了TripDetailPage的onDayAdded()

任何想法如何使我的UI更新时,取回完成?我是否需要以某种方式使用useState?

我相信这是react-query的预期行为。

您可以使用queryClient.invalidateQueries来重新获取缓存中的单个或多个查询,或者使用queryClient.setQueryData来立即更新查询的缓存数据。

如果你愿意,你可以在onSuccess回调中直接使用它。

见文档。

我相信我有一个解决办法。在我的PlannerBoard中,我需要通过useEffect检测道具的变化,然后再次手动设置状态为:

useEffect(() => {
const data: IData = createBoardData(props.days)
setState(data)
}, [props])

强制视图重新渲染。

可能不需要将props中的数据复制到PlannerBoard组件中的state中。正如你所发现的,这是你的问题,因为你创造了一个"拷贝"。你的州。这通常是一个反模式,您找到的修复方法(与useEffect同步)也不是一个好主意。我有一个关于这个话题的博客系列:

  • https://tkdodo.eu/blog/dont-over-use-state
  • https://tkdodo.eu/blog/putting-props-to-use-state

最简单的修复方法是从useState移动到useMemo,除非您确实需要setState方法,这在您的示例中没有显示:

const PlannerBoard = (props: TDndPlannerProps) => {  
const state = React.useMemo(() => {
// lots of processing
}, [props.days])

最新更新