Re-render ENTIRE website when a value changes
Unanswered
Sage Thrasher posted this in #help-forum
Sage ThrasherOP
using v15, i have a "select server" dropdown which lists all the servers available in the database.
the way i do this is: in root layout, i fetch all servers, parse them into a provider, the provider then picks the first server by default and sets that as the server.
whenever i change the server through this dropdown, i essentially want to refresh all the content of the page to use the new server, showing loaders etc to visually show the "change over" between servers. another thing, if theres no servers in the database, the user will have to create one and once i create one, its not being selected on router.refresh because its stored in a provider use state. how should i go about doing this? the entire rendering of the website is relied on this one value and im not sure the most effective way to do it.
essentially, it will be like a language selector which refreshes the website. im not updating the URL, i want it to be a simple dropdown which serves on the base route
the way i do this is: in root layout, i fetch all servers, parse them into a provider, the provider then picks the first server by default and sets that as the server.
whenever i change the server through this dropdown, i essentially want to refresh all the content of the page to use the new server, showing loaders etc to visually show the "change over" between servers. another thing, if theres no servers in the database, the user will have to create one and once i create one, its not being selected on router.refresh because its stored in a provider use state. how should i go about doing this? the entire rendering of the website is relied on this one value and im not sure the most effective way to do it.
essentially, it will be like a language selector which refreshes the website. im not updating the URL, i want it to be a simple dropdown which serves on the base route
/
29 Replies
@Sage Thrasher using v15, i have a "select server" dropdown which lists all the servers available in the database.
the way i do this is: in root layout, i fetch all servers, parse them into a provider, the provider then picks the first server by default and sets that as the server.
whenever i change the server through this dropdown, i essentially want to refresh all the content of the page to use the new server, showing loaders etc to visually show the "change over" between servers. another thing, if theres no servers in the database, the user will have to create one and once i create one, its not being selected on router.refresh because its stored in a provider use state. how should i go about doing this? the entire rendering of the website is relied on this one value and im not sure the most effective way to do it.
essentially, it will be like a language selector which refreshes the website. im not updating the URL, i want it to be a simple dropdown which serves on the base route `/`
Brown bear
If you wrap <App> in a Context.Provider then all the components reading from Context will re-render automatically when the state of Context changes. There's no need to use
router.refresh()
Sage ThrasherOP
to help better understand, this is the UI. all of the content within the pink would be unique to each server
@Brown bear If you wrap <App> in a Context.Provider then all the components reading from Context will re-render automatically when the state of Context changes. There's no need to use `router.refresh()`
Sage ThrasherOP
im fetching the servers in the layout and parsing them into the context provider to be able to access them in other components, im refreshing to update the servers fetched in the layout
if theres no servers and i create one, it should automatically re-render the page and the first server (the new one) should be selected without having to do setServer but its using useState hence why it isnt so im not sure of a better approach for this
Brown bear
Do not use layout level fetching. Use server actions.
'use server'
import { revalidatePath } from 'next/cache'
import { db } from '@/lib/db'
import type { Server } from '@/types/server'
export async function getServers(): Promise<Server[]> {
return db.servers.findMany()
}
export async function addServer(server: Server): Promise<Server> {
const newServer = await db.servers.create({
data: server
})
revalidatePath('/')
return newServer
}
export async function updateServer(server: Server): Promise<Server> {
const updatedServer = await db.servers.update({
where: { id: server.id },
data: server
})
revalidatePath('/')
return updatedServer
}
Sage ThrasherOP
what difference would this make?
Brown bear
And in your context you can do:
'use client'
import { createContext, useContext, useState, useEffect } from 'react'
const ServerContext = createContext(undefined)
export function ServerProvider({
children,
initialServers
}) {
const [currentServer, setCurrentServer] = useState(
initialServers[0] || null
)
useEffect(() => {
if (currentServer && !initialServers.find(s => s.id === currentServer.id)) {
setCurrentServer(initialServers[0] || null)
}
}, [initialServers, currentServer])
return (
<ServerContext.Provider value={{
currentServer,
setCurrentServer
}}>
{children}
</ServerContext.Provider>
)
}
@Sage Thrasher what difference would this make?
Brown bear
You can update your server list directly
Currently what you're doing is
- Layout fetches data
- Data goes through context
- Component triggers an update
- Manual router.refresh() reloads the layout
- Data flows through context again
- Layout fetches data
- Data goes through context
- Component triggers an update
- Manual router.refresh() reloads the layout
- Data flows through context again
If you do a server action:
- Component directly calls the server action
- Data is revalidated
- Affected components re-render automatically
- Component directly calls the server action
- Data is revalidated
- Affected components re-render automatically
And you don't have to re-render all the webpage, but only the components that changed
You can also correct loading states for a better user experience
@Brown bear You can also correct loading states for a better user experience
Sage ThrasherOP
what do you mean by "correct"?
Brown bear
have correct*
@Brown bear have correct*
Sage ThrasherOP
ah okay
so i've made the changes you've recommended me, just the last thing now is when i delete all the servers in my database then create a new one, it's not defaulting to the newly created one because the useState is ofc persistent across re-renders. im assuming i'd have to forcefully update it using
setCurrentServer
exported from the provider and theres not a better more effective way?Brown bear
You can put a useEffect in your Context
useEffect(() => {
if (servers.length > 0 && !currentServer) {
setCurrentServer(servers[0])
}
}, [servers, currentServer])
as mentioned above
this should work as long as you're calling the correct
deleteServers()
actioni dont suppose its possible to fetch data for the server on the server and use suspense to render loaders as the current server is stored in a state in the client, is there a more effective way where i could be able to access the current server from the server too? or would i need to use cookies to set the current selected server
Sage ThrasherOP
then i suppose i might as well put the server in the url
Brown bear
yeah you can use cookies or query params
@Brown bear yeah you can use cookies or query params
Sage ThrasherOP
is it possible to update cookies on the client?
Brown bear
with a server action you can access next/headers and set the cookies there
@Brown bear with a server action you can access next/headers and set the cookies there
Sage ThrasherOP
then i assume i'd also have to revalidate
/
path to ensure the page re-renders when changing my current server