Alternative for Svelte 5 store initialization in load() #14612
-
I am creating a frontend for the openai api in Svelte 5. It's basically a simple version of LibreChat. Currenlty I am using a store that contains the messages of a conversation. I set the store in the load() of my +page.ts. As I understand it, I should not be setting the store in a load() so I am looking for an alternative method. Here is some more information: message-store.svelte.ts: export const messageStore: { messages: Message[] } = $state({ messages: [] }); The store is set in the export const load: PageLoad = async ({ params, fetch }) => {
const response = await fetch(`/api/conversations/${params.slug}/messages`);
messageStore.messages = await response.json();
} Then When i click another conversation it triggers a state change params.slug and thus messageStore.messages are also updated. messageStore.messages.push(newMessage);
addMessage(newMessage);
const addMessage = (message: Message) => {
fetch(`/api/conversations/${conversationId}/messages`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ message }),
});
}; When chatGPT answers, I receive an EventStream and then I create a new message in the messageStore for the first item of the stream, then add content for that message for each item in the event stream in the store. When the event stream is done i call All of this works but I feel like there is room for improvement here. There is another implementation that also worked for me. ...
const messages = await response.json();
return {
conversationId,
messages,
}; Then I pass the let {
conversationId,
messagesHistory,
}: { conversationId?: number; messagesHistory: Message[] } = $props();
let messages: Message[] = $state([]);
onMount(() => {
messages = messagesHistory;
});
$effect(() => {
if (
!messages.length ||
!conversationId ||
messages[0].conversation_id === conversationId
)
return;
messages = messagesHistory;
}); And also of course I also changed Is that better? I know I also have experience in react and in react I would just do this: const MessageList: React.FC<MessageListProps> = ({ messagesHistory }) => {
const [messages, setMessages] = useState<Message[]>([]);
useEffect(() => {
setMessages(messagesHistory);
}, [messagesHistory]); That seems more elegant because the useEffect is only called when messageHistory is changed. Which is exactly the right time because it means the user clicked on another conversation. I have just started my journey with Svelte 5 and I am loving it so far and I feel there must be a solution that is at least as elegant as that. Either with a store or another way. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
You should not use global state as soon as your application has some server component (e.g. SSR) and separate users. The state will be shared between users on the server which can lead to data leaks. So you generally should return data directly from The additional You should not need to manually synchronize local state variables unless you change them locally for some reason. In this example, if you add messages via a form action with enhance, that would automatically invalidate the data, thus re-invoke Very simplified example of a page showing messages: // +page.server.ts
import type { Actions } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { api } from './api';
export const load = (async ({ params }) => {
const id = +params.id;
return { messages: await api.getMessages(id) };
}) satisfies PageServerLoad;
export const actions: Actions = {
async add({ params, request }) {
const data = await request.formData();
const message = data.get('message') as string;
const id = +params.id!;
await api.addMessage(id, message);
return {}; // could e.g. add message for `form` prop here
}
} <!-- +page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
let { data } = $props();
</script>
{#each data.messages as message}
<div>{message}</div>
{/each}
<form action="?/add" method="post" use:enhance>
<textarea name="message"></textarea> <br>
<button>Add</button>
</form> |
Beta Was this translation helpful? Give feedback.
-
Thank you, I now have several options that are better than my current implantation. 👍 |
Beta Was this translation helpful? Give feedback.
You should not use global state as soon as your application has some server component (e.g. SSR) and separate users. The state will be shared between users on the server which can lead to data leaks.
So you generally should return data directly from
load
functions.The additional
onMount
should be skippable;$effect
callbacks run on mount, so usually a single$effect
to update the local state would be enough. Theif
statement seems a bit redundant, the page also should only receive messages relevant to the conversation. (The ID from the URL can be accessed via$page.params
and does not need to be passed byload
.)You should not need to manually synchronize local state variables unless you…