Incremental data loading with useActionState and yield in Nextjs 15
Table of contents
A few days ago, I had a fucking dream that useActionState can use yield to push data. It was probably that state was an array []. Every time data was yielded in the action function, the data would be appended to the state. (It could also be that I seemed to have seen the code for this implementation somewhere, but I couldn't find it after searching for a whole day.)
Coincidentally, my business scenario today coincided with this implementation method, and I also successfully implemented Incremental data with useActionState and yield in Nextjs 15.
Final effect
Code
// actions.ts
'use server'
export async function* fetchDataAction() {
yield { index: 1, message: 'Step1', error: null }
await new Promise((resolve) => setTimeout(resolve, 1000))
yield { index: 2, message: 'Step2', error: null }
await new Promise((resolve) => setTimeout(resolve, 1000))
yield { index: 3, message: 'Step3', error: null }
await new Promise((resolve) => setTimeout(resolve, 1000))
yield { index: 4, message: 'Step4', error: null }
await new Promise((resolve) => setTimeout(resolve, 1000))
yield { index: 5, message: 'Step5', error: null }
await new Promise((resolve) => setTimeout(resolve, 1000))
yield { index: 6, message: 'Step6', error: null }
await new Promise((resolve) => setTimeout(resolve, 1000))
yield { index: 7, message: 'Step7', error: null }
}
// page.tsx
'use client'
import { useActionState, useEffect, useState } from 'react'
import { fetchDataAction } from '~/actions'
type PageProps = {}
const Page = ({}: PageProps) => {
const [currentStep, setCurrentStep] = useState<{
index: number | null | undefined
message: string | null | undefined
error: string | null | undefined
}>({ index: 0, message: null, error: null })
const [loading, setLoading] = useState<boolean>(true)
const [state, formAction] = useActionState(fetchDataAction, undefined)
useEffect(() => {
const stepItr = async () => {
const next = await state?.next()
const value = next?.value
const done = next?.done
if (done) {
setLoading(false)
return
}
setCurrentStep({ index: value?.index, message: value?.message, error: value?.error })
console.log(value)
stepItr()
}
if (state) {
stepItr()
setLoading(true)
}
}, [state])
return (
<div>
<p>
Current Step Index: {currentStep?.index || 'No Index...'}
</p>
<p>
Current Step Message: {currentStep?.message || 'No Message...'}
</p>
<p>
Current Step Error: {currentStep?.error || 'No Error...'}
</p>
<button
formAction={formAction}
>
Start
</button>
</div>
)
}
export default Page
I'm not sure if this will be a problem in production, if there are any please reply to point them out.
Subscribe to my newsletter
Read articles from Asiones Jia directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by