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.”

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]; //...