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


