Next.js Discord

Discord Forum

Revalidate Path Not Working As Expected

Answered
Northeast Congo Lion posted this in #help-forum
Open in Discord
Northeast Congo LionOP
Hello!
const sendMessage = useCallback(() => {
        axios.post('/api/conversations', {
            userId: userData.id,
            isGroup: false,
        })
        .then((data) => {
            console.log(data);
            router.push(`/users/conversations/${data.data.conversation.id}`);

            // Trigger Socket Event that will send conversation to all users
            if (data.data.type == "new") {
                const conversation = data.data.conversation;
                const emailList = conversation.participants.map((user) => { return user.email});
                socket.emit("conversation:new", {conversation: conversation, emailList:emailList});
            }
        })
    }, [userData, router]);

I am making a chat like app like whatsapp. My current project layout goes like this users/conversation/[conversationId]. I have a layout for conversation where I fetch the conversations like below
export default async function ConversationsLayout({children}: {children: React.ReactNode;}) {
    const users = await getUsers();
    const conversations = await getConversations();


    return (
     ...
                <ConversationList
                    initialItems={conversations}
                />
       ...

The problem is that when I make the api call I expect the database to have the new conversation. Thus when I visit the path with the .then statement I push my router to visit the new conversation page at users/conversation/[conversationId]. The problem is that on redirect the old conversationlist without the newly created conversation is present. I tried using revalidatePath in the api call in order to revalidate the revalidatePath( users/conversation, layout ). However this does not seem to work. I was wondering if there was a way to force the conversation layout to rerender so my data fetch function can handle/render the new conversation.
Answered by Ray
^
while use revalidatePath in server action, it contains the new rsc payload in the response and react will update the UI with the rsc payload accordingly and also update the router cache
View full answer

47 Replies

@Northeast Congo Lion Hello! const sendMessage = useCallback(() => { axios.post('/api/conversations', { userId: userData.id, isGroup: false, }) .then((data) => { console.log(data); router.push(`/users/conversations/${data.data.conversation.id}`); // Trigger Socket Event that will send conversation to all users if (data.data.type == "new") { const conversation = data.data.conversation; const emailList = conversation.participants.map((user) => { return user.email}); socket.emit("conversation:new", {conversation: conversation, emailList:emailList}); } }) }, [userData, router]); I am making a chat like app like whatsapp. My current project layout goes like this users/conversation/[conversationId]. I have a layout for conversation where I fetch the conversations like below export default async function ConversationsLayout({children}: {children: React.ReactNode;}) { const users = await getUsers(); const conversations = await getConversations(); return ( ... <ConversationList initialItems={conversations} /> ... The problem is that when I make the api call I expect the database to have the new conversation. Thus when I visit the path with the .then statement I push my router to visit the new conversation page at users/conversation/[conversationId]. The problem is that on redirect the old conversationlist without the newly created conversation is present. I tried using revalidatePath in the api call in order to revalidate the revalidatePath( users/conversation, layout ). However this does not seem to work. I was wondering if there was a way to force the conversation layout to rerender so my data fetch function can handle/render the new conversation.
try turning /api/conversations to a server action
// action.ts
'use server'
export async function createConversations({userId, isGroup}) {
   // insert into db
   const data = 

   revalidatePath(`/users/conversations`)
   return data
}

because using revalidatePath in route handler, the invalidation will happen on next visit
Northeast Congo LionOP
ill try that rq
  const sendMessage = useCallback(() => {
    createConversations({
        userId: userData.id,
        isGroup: false,
    })
    .then((data) => {
        console.log(data);
        router.push(`/users/conversations/${data.data.conversation.id}`);

        // Trigger Socket Event that will send conversation to all users
        if (data.data.type == "new") {
            const conversation = data.data.conversation;
            const emailList = conversation.participants.map((user) => { return user.email});
            socket.emit("conversation:new", {conversation: conversation, emailList:emailList});
        }
    })
}, [userData, router]);
Northeast Congo LionOP
im sorry did you change anything in this one?
im trying to see the changes but might be missing something
@Northeast Congo Lion im sorry did you change anything in this one?
yes I removed the redirection from server action
because I saw you have websocket in client
Northeast Congo LionOP
so instead of an axios call
its now a createConverations({}) server action?
yes
Northeast Congo LionOP
alright trying out rn
finally got it all typed out
const sendMessage = useCallback(() => {
createConversations({
userId: userData.id,
isGroup: false,
members: [],
name: "octagon"
})
.then((data) => {
console.log(data);
router.push(/users/conversations/${data.data.conversation.id});

// Trigger Socket Event that will send conversation to all users
if (data.data.type == "new") {
const conversation = data.data.conversation;
const emailList = conversation.participants.map((user) => { return user.email});
socket.emit("conversation:new", {conversation: conversation, emailList:emailList});
}
})
}, [userData, router]);

const sendMessage = useCallback(() => {
createConversations({
userId: userData.id,
isGroup: false,
members: [],
name: "octagon"
})
.then((data) => {
console.log(data);
router.push(/users/conversations/${data.data.conversation.id});

// Trigger Socket Event that will send conversation to all users
if (data.data.type == "new") {
const conversation = data.data.conversation;
const emailList = conversation.participants.map((user) => { return user.email});
socket.emit("conversation:new", {conversation: conversation, emailList:emailList});
}
})
}, [userData, router]);
uh for some reason i get an error that states
Unhandled Runtime Error
Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.

Call Stack
<unknown>
file:///C:/Users/trick/Documents/blackice/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (35:277727)
Object.toJSON
file:///C:/Users/trick/Documents/blackice/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (35:280578)
stringify
<anonymous>
<unknown>
file:///C:/Users/trick/Documents/blackice/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (35:266934)
eW
file:///C:/Users/trick/Documents/blackice/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (35:267013)
ez
file:///C:/Users/trick/Documents/blackice/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (35:267413)
Timeout._onTimeout
file:///C:/Users/trick/Documents/blackice/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (35:263912)
listOnTimeout
node:internal/timers (573:17)
process.processTimers
node:internal/timers (514:7)
i dont see where im passing in null anywhere
ill try adding an if before to see if userData.id is not null before to check if that fixes it
Northeast Congo LionOP
the data object?
does it contain a date?
yes
Northeast Congo LionOP
it doesent print to console
the error prevents it from getting to that point
return NextResponse.json({conversation: newConversation, type: "new"});
Its probably because im sending the whole conversation object back from the server component
and objects arent serializable. However I need that converation to send to the websocket so other users can use it
Northeast Congo LionOP
no psql but im using prisma to fetch the db from my database if that matters
just transform the object to be serializable
Northeast Congo LionOP
like .JSON it?
no
const newData = {
  id: data.id,
  date: data.date.toISOString()
  ...
}
return newData
Northeast Congo LionOP
const newConversation = await prisma.conversation.create({
data: {
participants: {
connect: [
{ id: currentUser.id },
{ id: userId }
]
},
isGroup: false,
},
include: {
participants: true
}
});

alright this might be why
im including other objects inside of my conversation as well
thats the data from my server action
const { json, meta } = superjson.serialize(newConversation);
return json
Northeast Congo LionOP
alright trying out this
revalidatePath(/users/conversations);
const { json, meta } = SuperJSON.serialize({conversation: newConversation, type: "new"});
return json
works!
getting data let me see rq if the revalidate works now
in the meantime can i ask why changing from an api call to a server action would make a difference? Is that best practice to use server actions instead of api calls?
also just tried everything and it works well! Thanks a ton!!!
Answer
using revalidatePath in route handler, the invalidation will happen on next visit
Therefore, always use server action with revalidatePath/revalidateTag if you need to deal with the cache
Northeast Congo LionOP
oh so it triggers the rerender with the rsc that makes alot more sense
once again thanks sm! you just made my birthday!