I have a page with a form. I want to store the values of the form in the url so they can persist even if you accidentally refresh the page.
// Form.tsx
"use client"
export default function Form(){
const pathname = usePathname()
const searchParams = useSearchParams()
const router = useRouter()
const [content, setContent] = useState(searchParams.get("content") || "")
useEffect(() => {
const newSearchParams = new UrlSearchParams({ content })
router.replace(pathname + "?" + newSearchParams.toString(), { scroll: false })
}, [content, pathname, router])
return <div>
// Form ui here
</div>
}
// page.tsx
export default async function Page(){
const data = await getData() // fetches data from db
return <div>
// Some stuff is rendered based on data
<Form/>
</div>
}
The problem I'm having is that every time I add url search params, the page calls getData(), causing unnecessary fetching. Is there a way to add search params without causing data to refetch?
I know that, most of the time, you would want data to refetch when the search params change, but I don't in this scenario.
I've tried turning the page into a client component instead, which works to call getData() only once, but that doesn't seem to allow me to add metadata to the page. I've also seen people say that pages should be server components as much as possible.
Edit: I think I've got it. I just needed to wrap all of my page content inside a client component and do all my fetching there.
// Inner.tsx
"use client"
export default function Inner(){
const [data, setData] = useState()
useEffect(() => {
getData().then(res => setData(res))
}, [])
return <div>
//content rendered here
<Form/>
</div>
}
// page.tsx
export default function Page(){
return <Inner/>
}
Okay, I figured out what I was doing wrong. I should have been using window.history.replaceState() directly instead of using NextJS's router.replace().
Lesson learned. The Next router should be used for routing logic, and not for quietly modifying the url.
// wrong
router.replace(pathname + "?" + newSearchParams.toString(), { scroll: false })
// correct
window.history.replaceState(null, "", pathname + "?" + newSearchParams.toString())
I decided to wrap all the contents inside an inner client component instead and do all my fetching there. This seems to work and the data only fetches once now.
// Inner.tsx
"use client"
export default function Inner(){
const [data, setData] = useState()
useEffect(() => {
getData().then(res => setData(res))
}, [])
return <div>
//content rendered here
<Form/>
</div>
}
// page.tsx
export default function Page(){
return <Inner/>
}
I want to store the values of the form in the url so they can persist even if you accidentally refresh the page
You can try to store in Localstorage
every time I add url search params, the page calls getData(), causing unnecessary fetching
It's because, of useEffect's dependencies which executes, router.replace().
useEffect(() => {
const newSearchParams = new UrlSearchParams({ content })
router.replace(pathname + "?" + newSearchParams.toString(), { scroll: false })
}, [content, pathname, router])
I've tried turning the page into a client component instead, which works to call getData() only once,.... I've also seen people say that pages should be server components as much as possible.
autocomplete="on" in form tag, so that browser can
help in quick form filling.Solution :
Keep page server-side & import that form component (this is a client side component, at top use "use client") in page. In
local storage store the form state, by making a object . Ex. form:{field1 : value1,field2 : value2}. localStorage key = form, value = Object.
In useEffect, check if form key exists, if exists then get it from localstorage and parse values & set them as default values in form fields, else if it doesn't exists just keep them empty.
Please Read :
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With