Chat Bot with AI

I have created a React app “Chat Bot App“, which allows user to chat with AI. It is a part if the udemy.com course: “React JS: Build 6 Real-World React Apps with AI Integration.”

Eva's Own Chat AI

With the help of great instructions on dev.to by Douglas Toledo I sucessfully deployed the app on Github (I had to adjust the integration of the openai.com secret API key.)

Moreover, in my app, there are some differences compared to the course:

Removing unnecessary use of the UseEffect Hook

As per React’s official docs “You Might Not Need an Effect”:

Effects are an escape hatch from the React paradigm. They let you “step outside” of React and synchronize your components with some external system like a non-React widget, network, or the browser DOM. If there is no external system involved (for example, if you want to update a component’s state when some props or state change), you shouldn’t need an Effect. Removing unnecessary Effects will make your code easier to follow, faster to run, and less error-prone.

Example of replacing the useEffect hook

In my opinion, in the original program, the usage of the useEffect hook is not necessary. Here, useEffect runs when states change (activeChatId, chats):

  useEffect(() => {
    //...
  }, [activeChatId, chats]);

The use Effect itself then changes another state (messages):

  useEffect(() => {
    const activeChatObject = chats.find(chat => chat.id === activeChatId);
    setMessages(activeChatObject ? activeChatObject.messages : []);
  }, [activeChatId, chats]);

This causes additional re-renders, because useEffect runs after rendering the component.

Instead, it is sufficient to update messages with setMessages call every time the activeChatId or chats are updated. This happens in three cases:

1. When a new chat is created

Here I had to refactor the program a bit. State chats belong to the parent App, while state messages belong to the child ChatBotApp. I lifted the state variable up – from child to parent. And I added handling the change of messages when a new chat is created:

//App.jsx
const [messages, setMessages] = useState(chats[activeChatId]?.messages || []);
// ...
  const createNewChat = () => {
    //...
    };
    
    const updatedChats = [newChat,...chats]
    setChats(updatedChats) 
    setActiveChatId(newChat.id)
// ADDED
    const activeChatObject = updatedChats.find(chat => chat.id === newChat.id);
    setMessages(activeChatObject ? activeChatObject.messages : []);
//ADDED
    
  }

2. When a different chat is selected

Here I simply added the commands of the useEffect hook:

  const handleSelectChat = (id) => {
    setActiveChatId(id);
/* ADDED START*/
    const activeChatObject = chats.find(chat => chat.id === activeChatId);
    setMessages(activeChatObject ? activeChatObject.messages : []);
/* ADDED END*/
  }

3. When a message is sent

The changes are similar.

Simplifying the program itself

In the original program, new chat is created with the following function:

  const createNewChat = (initialMessage = '') => {
    const newChat = {
      id: uuidv4(),
      displayId: `Chat ${new Date().toLocaleDateString(
        'en-GB'
      )} ${new Date().toLocaleTimeString()}`,
      messages: initialMessage ? [{ type: 'prompt', text: initialMessage,
        timestamp: new Date().toLocaleTimeString(), }] : [],
    };
    const updatedChats = [newChat,...chats]
    setChats(updatedChats)
    setActiveChatId(newChat.id)
  }

The optional parameter initialMessage is the text of the first message in the chat. However, this optional new message is not necessary, since the same construction follows after a few lines of code in the function anyway, only in a different scenario:

// ...  
    if (!activeChatId) {
      onNewChat(inputValue)  
    }
    else 
    {
      const newMessage = {
        type: 'prompt',
        text: inputValue,
        timeStamp: new Date().toLocaleTimeString(),
      };
      const updatedMessages = [...messages, newMessage];
//...

So I simplified the process:

  const createNewChat = () => {
    const newChat = {
      id: uuidv4(),
      displayId: `Chat ${new Date().toLocaleDateString(
        'en-GB'
      )} ${new Date().toLocaleTimeString()}`,
      messages: [],
    };
 //...
  }
// ...  
    if (!activeChatId) {
      onNewChat()  
    }  
      const newMessage = {
        type: 'prompt',
        text: inputValue,
        timeStamp: new Date().toLocaleTimeString(),
      };
      const updatedMessages = [...messages, newMessage];
//...