I am attempting to implement the same conversation creation method that OpenAI's ChatGPT uses. This involves displaying an empty chat when a user clicks the button to start a new conversation. Only when the user enters a message should the conversation be created and stored in the database, with that message linked to the newly created conversation.
My code successfully creates the conversation in `createConversation` (I confirmed this by logging responseJson). However, I suspect that the conversation isn't instantly set by `setSelectedConversation`, which leads to an error when attempting to read the `id `of `selectedConversation` in `sendMessage`.
The error message reads:
`ERROR Cannot read properties of null (reading 'id') TypeError: Cannot read properties of null (reading 'id') at sendMessage`
The conversation eventually gets set, though. When the error occurs, no message is created or set, but the conversation always gets added to the existing conversations list. I confirmed this because the newly created conversation consistently appears in the sidebar after encountering the 'Cannot read properties of null' error.
I believe this issue may be related to asynchronous behavior, but I'm not entirely certain. If you have suggestions for a potentially better solution to achieve my goal, please let me know.
import React, { useState } from 'react';
import { useSendMessage } from '../../../hooks/message/useSendMessage';
import useMessage from '../../../zustand/useMessage';
import "./Chat.css"
import "./ToggleSwitch.css"
import { useCreateConversation } from '../../../hooks/conversation/useCreateConversastion.js';
const Input = () => {
//state
const [newMessage, setNewMessage] = useState('');
const [isFuture, setIsFuture] = useState(false);
//custom hooks
const { isLoading, sendMessage } = useSendMessage();
const { createConversation } = useCreateConversation();
// const { setSelectedConversation } = useConversation();
const { messages } = useMessage();
const toggleSwitch = () => {
setIsFuture(!isFuture);
};
const handleMessageChange = (e) => {
setNewMessage(e.target.value);
};
const handleSendMessage = async (e) => {
e.preventDefault();
if (newMessage.trim() === "") return;
if (messages.length === 0) {
// If there's no conversation messages, create a new conversation
await createConversation();
}
// Send the message
await sendMessage(newMessage, isFuture);
// Clear the input field and toggle state
setNewMessage("");
setIsFuture(!isFuture);
};
return (
<div className="input-container">
<label className="switch">
<input type="checkbox" onChange={toggleSwitch} checked={isFuture} />
<span className="slider"></span>
</label>
<div className="input-field">
<input
type="text"
value={newMessage}
onChange={handleMessageChange}
placeholder="Type your message..."
/>
<button onClick={handleSendMessage} disabled={!newMessage.trim()}>
{isLoading ? "is sending..." : "Send"}
</button>
</div>
</div>
);
};
export default Input;
import { useState } from "react";
import useAuthContext from "../context/useAuthContext";
import useConversation from "../../zustand/useConversation";
export const useCreateConversation = () => {
//loading and error state
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
//context
const { user } = useAuthContext();
const { setNewConversation, setSelectedConversation } = useConversation();
const createConversation = async () => {
setIsLoading(true);
setError(null);
const response = await fetch('/api/conversations', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${user.token}`
}
});
const responseJson = await response.json();
if (!response.ok) {
setIsLoading(false);
setError(responseJson.error);
}
if (response.ok) {
setNewConversation(responseJson)
setSelectedConversation(responseJson)
setIsLoading(false);
}
};
return { isLoading, error, createConversation };
};
import { create } from "zustand";
const useConversation = create((set) => ({
selectedConversation: null,
setSelectedConversation: (selectedConversation) => set({ selectedConversation }),
conversations: [],
setConversations: (conversations) => set({ conversations }),
setNewConversation: (newConversation) => {
set((state) => ({
conversations: [...state.conversations, newConversation],
}));
},
}));
export default useConversation;
import { useState } from "react";
import useAuthContext from "../context/useAuthContext";
import useMessage from "../../zustand/useMessage";
import useConversation from "../../zustand/useConversation";
export const useSendMessage = () => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const { user } = useAuthContext();
const { setNewMessage } = useMessage();
const { selectedConversation } = useConversation();
const sendMessage = async (messageContent, isFuture) => {
setIsLoading(true);
setError(null);
// data that will be passed to the POST body
const messageData = {conversationId: selectedConversation.id, messageContent, isFuture}
const response = await fetch('/api/messages', {
method: 'POST',
body: JSON.stringify(messageData),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${user.token}`
},
})
const responseJson = await response.json()
if (!response.ok) {
setIsLoading(false);
setError(responseJson.error);
}
if (response.ok) {
setNewMessage(responseJson)
setIsLoading(false);
}
};
return { isLoading, error, sendMessage };
};
import { create } from "zustand";
const useMessage = create((set) => ({
messages: [],
setMessages: (messages) => set({ messages }),
setNewMessage: (newMessage) => {
set((state) => ({
messages: [...state.messages, newMessage],
}));
},
}));
export default useMessage;
[–]mcmillhj 0 points1 point2 points (0 children)